#!/user/bin/env python3
# -----------------------------------------------
#
# Game run enviroumnet: Firefox
# 分辨率: 1920*1080
# 操作系统： ubuntu 20.04
# 浏览器： chrome， firefox
# 作者: xiedaolin 2020-5-14
# -----------------------------------------------
import json
import os
import pathlib
import pyautogui
import fire
import random
import pytesseract
import time
import configparser
import atexit
import numpy as np

from PIL import Image, ImageGrab
# from threading import Timer

# **********使用Python的日志功能**********begin
import logging
from logging.handlers import TimedRotatingFileHandler
logFileName = "log/GoTAuto.log"
logger = logging.getLogger("GoTAuto")
handlerConsole = logging.StreamHandler()
# handlerFile = logging.FileHandler(filename=logFileName, when="D")
# D-每天一份日志，midnight-每天一份日志，0晨切换
handlerFile = TimedRotatingFileHandler(
    filename=logFileName, when="midnight", interval=1, encoding="utf-8")
logger.setLevel(logging.DEBUG)
handlerConsole.setLevel(logging.INFO)
handlerFile.setLevel(logging.DEBUG)
formatter = logging.Formatter(
    "[%(asctime)s %(name)s %(levelname)s]>>> %(message)s")
handlerConsole.setFormatter(formatter)
handlerFile.setFormatter(formatter)
logger.addHandler(handlerConsole)
logger.addHandler(handlerFile)
# **********使用Python的日志功能**********end


def pxlMatch(x, y, expectedRGBColor, tolerance=0, comment=""):
    matched = pyautogui.pixelMatchesColor(x, y, expectedRGBColor, tolerance)
    realRGB = pyautogui.pixel(x, y)
    logger.debug("【{}】像素匹配: 坐标({},{})，期望RGB=({})，实际RGB={}，偏差={}，匹配结果={}".format(
        comment, x, y, expectedRGBColor, realRGB, tolerance, matched))
    return matched


def updateParamDict(config='config.ini'):
    """从配置文件中读取配置到字典对象"""
    # 创建配置文件对象
    cfg = configparser.ConfigParser()

    # 读取文件
    cfg.read(config, encoding='utf-8')
    para = {}
    sections = cfg.sections()
    for section in sections:
        para[section] = {}
        items = cfg.items(section)
        # 首先读取每个节点下对应配置的数据类型
        dataTypeStr = cfg.get(section, "data_type", fallback=None)
        dataTypeDict = {}
        if dataTypeStr:
            dataTypeList = dataTypeStr.replace(
                '\n', '').replace(' ', '').split(',')
            dataTypeTupleList = [tuple(v.split(':')) for v in dataTypeList]
            dataTypeDict = dict(dataTypeTupleList)
        for item, value in items:
            if item == "data_type":
                continue
            # 判断数据类型，采用不同的函数读取
            # dataType = cfgItemValueType.get("{}.{}".format(section,item), "iTuple")
            dataType = dataTypeDict.get(item, 'iTuple')
            if dataType == "int":
                dictValue = int(value)
            elif dataType == "float":
                dictValue = float(value)
            elif dataType == "bool":
                # 下列值视为 True: '1', 'yes', 'true', 'on' 其它都是False
                value = value.upper()
                dictValue = True if value == 'TRUE' or value == '1' or value == 'YES' or value == 'ON' else False
                # bool(value)
            elif dataType == "str":
                dictValue = value
            elif dataType == "sList":
                dictValue = value.replace(' ', '').split(',')
            elif dataType == "iTuple":
                # 逗号分隔的数字转化为元组
                valueList = value.replace(' ', '').split(',')
                intValueList = [int(x) for x in valueList]
                dictValue = tuple(intValueList)
            else:
                raise ValueError(
                    "未找到配置项对应的数据类型，无法读取对应值。配置项名称：{}.{}".format(section, item))

            para[section][item] = dictValue

    return para


def getFullScreen():
    """获取当前的界面是否是全屏的状态"""
    # searchRegion = (0, 0, 500, 400)
    searchRegion = config['getFullScreen']['searchregion']
    findPos = pyautogui.locateOnScreen(
        "scrShot-browser/headImage.png", confidence=0.9, region=searchRegion)
    if not findPos:
        logger.error("未能检测到GOT的游戏界面。")
        return False
        # logger.exception("No detect GOT.")
        # raise Exception("No detect GOT.")

    if findPos.left < 40 and findPos.top < 90:
        currentFullScreen = True  # 当前已经是全屏状态
    else:
        currentFullScreen = False
    logger.debug("获取当前的浏览器界面是否全屏状态:{}".format(currentFullScreen))
    return currentFullScreen


def setFullScreen(full=True):
    """让浏览器的界面全屏化，False 则退出全屏。"""

    logger.debug("开启全屏模式: {}。".format(full))

    currentFullScreen = getFullScreen()

    if full and currentFullScreen:
        logger.debug("已经在全屏状态下了。")
        return True

    # 定位到全屏/正常化 按钮的位置
    # searchRegion = (0, 0, 600, 210)
    searchRegion = config['setFullScreen']['searchregion']
    findPos = pyautogui.locateCenterOnScreen(
        "./scrShot-browser/fullScreenButton.png", confidence=0.9, region=searchRegion)
    findPos2 = pyautogui.locateCenterOnScreen(
        "./scrShot-browser/fullScreenButton2.png", confidence=0.9, region=searchRegion)

    findPos = findPos or findPos2
    if findPos and (full != currentFullScreen):
        pyautogui.click(findPos.x, findPos.y, duration=DURATION)
        pyautogui.press('space')     # press the F1 key
        return True
    logger.warning("匹配不到全屏按钮的位置，无法设置全屏模式。")
    return False


def setWindowFocus():
    """设置主窗口获得焦点"""
    logger.debug("设置GOT的界面为主焦点窗口.")
    # 是否打开GOT浏览器页面
    GotTabFocus = pxlMatch(
        *config['setWindowFocus']['browsergottab'], config['setWindowFocus']['browsergottab_rgb'], tolerance=10, comment="GoT浏览器Tab页")
    if GotTabFocus:
        pyautogui.click(config['setWindowFocus']
                        ['browsergottab'], duration=DURATION)
        return True
    else:
        pyautogui.click(config['setWindowFocus']
                        ['taskbariconbrowser'], duration=DURATION)
        return False


def getMapMode():
    '''
    # 检测当前是什么地图模式【city, world】
        返回值：
            offline: 离线模式
            city: 城堡
            world: 世界地图
            loading: 切换过程中
            nothing: 未知
    '''
    currentMapMode = ""
    findOffline = pyautogui.locateOnScreen(
        'scrShot-browser/offline.png', confidence=0.9)
    if findOffline:
        currentMapMode = "offline"
        logger.debug("当前界面的地图模式为： {}".format(currentMapMode))
        return currentMapMode

    # 校验当前的地图模式
    # 判断是否city模式
    findcity = pyautogui.locateOnScreen(
        'scrShot-browser/map_city.png', confidence=0.9, region=config['getMapMode']['search_region'])

    # 判断是否世界模式
    findWorld = pyautogui.locateOnScreen(
        'scrShot-browser/map_world.png', confidence=0.9, region=config['getMapMode']['search_region'])

    # 判断是否正在加载过程
    findLoading = pyautogui.locateOnScreen(
        'scrShot-browser/customer_service.png', confidence=0.9, region=config['getMapMode']['search_region2'])

    if findcity and not findWorld:
        currentMapMode = "city"
    elif findWorld and not findcity:
        currentMapMode = "world"
    elif findLoading:
        currentMapMode = "loading"
    else:
        currentMapMode = "nothing"

    logger.debug("当前界面的地图模式为： {}".format(currentMapMode))
    return currentMapMode


def closeActiveWindow():
    """关闭当前界面所有活动窗口"""
    # logger.debug("利用快捷键[S]进行关闭窗口。")

    # start = round(time.time())

    # 关闭所有带 X 图标的窗口
    searchRegion = config['closeActiveWindow']['searchregion']

    for root, dirs, files in os.walk(".\\scrShot-browser\\close", topdown=True):
        for f in files:
            fullpath = os.path.join(root, f)
            pyautogui.press("S")
            findPosCenter = pyautogui.locateCenterOnScreen(
                fullpath, confidence=0.9, region=searchRegion)
            if findPosCenter:
                logger.debug("找到有窗口需要进行关闭，关闭按钮【{}】坐标位置x={},y={}".format(
                    f, findPosCenter.x, findPosCenter.y))
                pyautogui.click(findPosCenter.x,
                                findPosCenter.y, duration=DURATION)

    return True

    # while True:
    #     pyautogui.press("S")

    #     findPosCenter = pyautogui.locateCenterOnScreen("scrShot/buttonClose.png", confidence=0.9, region=searchRegion)
    #     if findPosCenter:
    #         logger.debug("找到有窗口需要进行关闭，窗口关闭按钮1【X】坐标位置x={},y={}".format(findPosCenter.x, findPosCenter.y))
    #         pyautogui.click(findPosCenter.x, findPosCenter.y,duration=DURATION)

    #     findPosCenter2 = pyautogui.locateCenterOnScreen("scrShot/buttonClose2.png", confidence=0.9, region=searchRegion)
    #     if findPosCenter2:
    #         logger.debug("找到有窗口需要进行关闭，窗口关闭按钮2【X】坐标位置x={},y={}".format(findPosCenter2.x, findPosCenter2.y))
    #         pyautogui.click(findPosCenter2.x, findPosCenter2.y, duration=DURATION)

    #     findPosCenter3 = pyautogui.locateCenterOnScreen("scrShot-browser/buttonClose3.png", confidence=0.9, region=searchRegion)
    #     if findPosCenter3:
    #         logger.debug("找到有窗口需要进行关闭，窗口关闭按钮3【X】坐标位置x={},y={}".format(findPosCenter3.x, findPosCenter3.y))
    #         pyautogui.click(findPosCenter3, duration=DURATION)

    #     findPosCenter4 = pyautogui.locateCenterOnScreen("scrShot-browser/buttonClose4.png", confidence=0.9, region=searchRegion)
    #     if findPosCenter4:
    #         logger.debug("找到有窗口需要进行关闭，窗口关闭按钮4【X】坐标位置x={},y={}".format(findPosCenter4.x, findPosCenter4.y))
    #         pyautogui.click(findPosCenter4, duration=DURATION)

    #     if not findPosCenter and not findPosCenter2 and not findPosCenter3 and not findPosCenter4:
    #         # logger.debug("未找到有活动窗口。")
    #         break

    #     if round(time.time()) - start > config["Global"]["timeout"]:
    #         logger.debug("关闭活动窗口超时。")
    #         restartGame()
    #         break


def restartGame():
    '''# 重新刷新当前页面，然后设置全屏最大化，最远视图模式'''
    logger.debug("利用浏览器的快捷键[F5]刷新页面。")
    setWindowFocus()
    # Press the Esc key. All key names are in pyautogui.KEY_NAMES
    pyautogui.press('esc')
    pyautogui.sleep(5)

    pyautogui.press("F5")
    pyautogui.sleep(5)

    # waitting
    timeOut = config['Global']['timeout']
    timeBegin = time.time()
    logger.debug("开始等待页面加载。。。 开始时间:{:.2f}".format(timeBegin))
    while True:
        # check the startup login screen
        # 判断是否刷新登入模式
        loginSuccess = pyautogui.locateCenterOnScreen(
            'scrShot-browser/loginScreen.png', confidence=0.9)
        loginSuccess1 = pyautogui.locateCenterOnScreen(
            'scrShot-browser/headImage.png', confidence=0.9)
        timeEnd = time.time()
        if loginSuccess or loginSuccess1:
            logger.debug("加载页面完成. 总共耗时 {:.2f} 秒。".format(timeEnd - timeBegin))
            # pyautogui.click(loginSuccess.x, loginSuccess.y, button="right")
            closeActiveWindow()
            setFullScreen(True)
            # 保证窗口界面是最远处视图
            setTopView()
            # break
            return True

        logger.debug("继续等待页面加载中......耗费的时间:{:.2f}秒".format(
            timeEnd - timeBegin))

        # time out
        if timeEnd - timeBegin > timeOut:
            logger.debug("页面加载超时. 耗费的时间超过设定的【{:.2f}】秒。".format(timeOut))
            break

        # pyautogui.sleep(1)
        setPause(5)

        # 关闭能发现的窗口
        closeActiveWindow()
        
        # 随机点击窗口界面，这样可以起到关闭窗口效果
        x, y, w, h = config['restartGame']['random_click_region']
        pyautogui.click((x + random.randint(0, w)), (y + random.randint(0, h)))

    return False


def setTopView():
    """set the view is far away."""
    pyautogui.click(SCREEN_MID_W, SCREEN_MID_H, clicks=1, button="right")
    pyautogui.press("D")
    pyautogui.sleep(2)
    pyautogui.press("S")
    # closeActiveWindow()
    # 保证窗口界面是最远处视图
    logger.debug("设置界面为最远视图。")
    pyautogui.scroll(-20)
    pyautogui.sleep(2)

    pyautogui.scroll(-20)
    pyautogui.sleep(2)

    # pyautogui.scroll(-20)


def switchMapMode(mapMode='city'):
    '''# 切换指定的地图模式'''
    logger.debug("开始进行地图模式切换：==> {}".format(mapMode))
    curMapMode = getMapMode()
    if curMapMode == mapMode:
        logger.debug("当前的地图模式就是{}，无需切换".format(curMapMode))
        return True

    #
    logger.debug("利用快捷键[M]改变地图模式.")
    pyautogui.press("M")

    # waiting when change the map mode
    TIME_OUT = config['Global']['timeout']
    start = time.time()
    loading = True
    while loading:
        logger.debug("切换地图模式[==>{}]切换中。。。。。。等待[{:.2f}]".format(
            mapMode, time.time() - start))
        # pyautogui.sleep(2)
        setPause(5)
        curMapMode = getMapMode()
        if curMapMode == "offline":
            logger.debug("在切换地图的时候，检测到当前为离线界面，账号在别的地方有登陆，不用等待了。")
            return False

        stop = time.time()
        cost = stop - start
        if curMapMode == mapMode:
            loading = False
            pyautogui.sleep(5)
            logger.debug(
                "地图切换成功，切换成功的地图模式：{}, total waitting time:{:.2f}.".format(mapMode, cost))
            return True

        if curMapMode in ["city", "world"] and curMapMode != mapMode:
            logger.debug("利用快捷键[M]改变地图模式.")
            pyautogui.press("M")

        if cost > TIME_OUT:
            logger.warning(
                "地图切换模式：{} 超时，总共花费的时间是:{:.2f} 截图日志。".format(mapMode, cost))
            loading = False
            pyautogui.screenshot(
                "log/screenshot-{}.png".format(time.strftime("%Y%m%d%H%M%S")))
            restartGame()  # 终极大法，重启游戏

    return False


def checkMerchantShip():
    '''# 检查商船 Merchant Ship'''
    logger.debug("开始检查商船是否有可以购买物品。")
    # 商船
    shipX, shipY = config['checkMerchantShip']['ship']
    # shipWindowCloseX, shipWindowCloseY = 1343, 190
    regionChecked = config['checkMerchantShip']['searchrangepurchase']
    pyautogui.click(shipX, shipY, duration=DURATION)
    pyautogui.sleep(2)  # 太快了，匹配不到图片
    i = 0
    for pos in pyautogui.locateAllOnScreen('scrShot/boat_buy_can.png', confidence=0.9, region=regionChecked):
        logger.debug("找到矿物资源足够支付且可购买的物品。x:{}, y:{}".format(pos.left, pos.top))
        posCenter = pyautogui.center(pos)
        pyautogui.click(posCenter.x, posCenter.y, clicks=2,
                        interval=1, duration=DURATION)
        i += 1
        # 满足4次，说明绝对成功。
        if i == 4:
            closeActiveWindow()
            return True

    start = round(time.time())
    # 保证需要补充资源道具的一次性买完，不要等待下一次，特别是粮食，一直在消耗掉。
    while True:
        findPos = pyautogui.locateCenterOnScreen(
            'scrShot/boat_buy_not.png', confidence=0.9, region=regionChecked)
        if findPos:
            logger.debug("找到可购买的物品，但是矿物资源不足，需要资源道具。x:{}, y:{}".format(
                findPos.x, findPos.y))
            not_engouh_OK_x, not_engouh_OK_y = config['checkMerchantShip']['not_engouh_ok']
            one_key_x, one_key_y = config['checkMerchantShip']['one_key']
            res_apply_x, res_apply_y = config['checkMerchantShip']['res_apply']
            pyautogui.click(findPos.x, findPos.y, duration=DURATION)
            pyautogui.click(not_engouh_OK_x, not_engouh_OK_y,
                            duration=DURATION)
            pyautogui.sleep(4)
            pyautogui.click(one_key_x, one_key_y, duration=DURATION)
            tmpPause = pyautogui.PAUSE
            pyautogui.PAUSE = 1.0
            pyautogui.click(res_apply_x, res_apply_y)
            pyautogui.click(findPos.x, findPos.y, clicks=2, interval=1)
            pyautogui.PAUSE = tmpPause
            # 粮食资源，在用道具补充的时候同时也在消耗，有可能点击购买的时候又资源不足了。给点时间反应下。
            pyautogui.sleep(10)
        else:
            break

        if round(time.time()) - start > config["Global"]["timeout"]:
            logger.debug("购买商船物品超时。")
            break

    # 关闭商船购买界面
    closeActiveWindow()

    # 再次打开看看是否购买成功
    pyautogui.click(shipX, shipY, duration=DURATION)
    pyautogui.sleep(2)  # 太快了，匹配不到图片
    findPos = pyautogui.locateCenterOnScreen(
        'scrShot/boat_buy_not.png', confidence=0.9, region=regionChecked)
    result = False if findPos else True
    logger.debug("再次打开商船是否还有有可以购买物品。result:{}".format(result))
    closeActiveWindow()
    return result


def checkTavernBonus():
    '''
    检查酒馆是否有可领取的奖励资源 酒馆 tavern  Bonus奖励
    '''
    # 检查是否可以领取
    logger.debug("检查酒馆是否有可领取的奖励资源.")

    # 酒馆
    tavernX, tavernY = config['checkTavernBonus']['tavern']

    # 打开酒馆收益界面
    pyautogui.click(tavernX, tavernY, duration=DURATION)
    pyautogui.sleep(2)  # 太快了，匹配不到图片

    searchRegion = config['checkTavernBonus']['searchregion']
    findPos = pyautogui.locateAllOnScreen(
        "scrShot/pubclaim.png", confidence=0.9, region=searchRegion, grayscale=False)
    # 匹配到表示可以领取
    for pos in findPos:
        logger.debug("酒馆收益已经可以领取,点击按钮坐标x={},y={}。".format(pos.left, pos.top))
        posCenter = pyautogui.center(pos)
        pyautogui.click(posCenter.x, posCenter.y, clicks=3,
                        interval=2, duration=DURATION)
        # pyautogui.click(pos.left, pos.top, duration=DURATION)

    closeActiveWindow()
    # pyautogui.click(pub_close_x, pub_close_y, duration=DURATION)


def trainingGrounds(model=2):
    """演武场
        1.指挥官比拼
        2.游兵远征
    return
        True: 执行成功
        False： 失败"""

    training_x, training_y = config['trainingGrounds']['training']
    training_model1_x, training_model1_y = config['trainingGrounds']['training_model1']
    training_model2_x, training_model2_y = config['trainingGrounds']['training_model2']
    model2RestartX, model2RestartY = config['trainingGrounds']['model2restart']
    pyautogui.click(training_x, training_y, duration=DURATION)
    pyautogui.sleep(2)

    if model == 1:
        logger.debug("每日任务>>>演武场指挥官比拼。")
        pyautogui.click(training_model1_x,
                        training_model1_y, duration=DURATION)
        pyautogui.sleep(4)
        # 挑战指挥官，随机一个指挥官进行挑战,每天有5次免费机会
        for i in range(5):
            commander = "commander{}".format(random.randint(1, 4))
            pyautogui.click(config['trainingGrounds']
                            [commander], duration=DURATION)
            pyautogui.sleep(3)
            # 弹出退出按钮说明挑战成功
            searchRegion = (SCREEN_MID_W, SCREEN_MID_H,
                            SCREEN_MID_W, SCREEN_MID_H)
            findPos = pyautogui.locateCenterOnScreen(
                "scrShot-browser/btn_quit.png", confidence=0.9, region=searchRegion)
            if findPos:
                logger.debug("演武场指挥官比拼，第{}次挑战{}完毕。退出按钮位置：{}".format(
                    i+1, commander, findPos))
                pyautogui.click(findPos, duration=DURATION)

        closeActiveWindow()
        return True

    if model == 2:
        logger.debug("每日任务>>>游兵远征关卡。")
        pyautogui.click(training_model2_x,
                        training_model2_y, duration=DURATION)
        # If the restart button enable
        searchRegion = config['trainingGrounds']['searchregion1']
        findPos = pyautogui.locateCenterOnScreen(
            "scrShot-browser/reddot.png", confidence=0.9, region=searchRegion)
        redDot = pxlMatch(
            *config['trainingGrounds']['red_dot_pos'], config['Global']['red_dot'], tolerance=10, comment="每日任务>>>游兵远征关卡。")
        # 重新刷新可用
        if findPos or redDot:
            pyautogui.click(model2RestartX, model2RestartY, duration=DURATION)
            searchRegion = config['trainingGrounds']['searchregion2']
            findPos = pyautogui.locateCenterOnScreen(
                "scrShot-browser/buttonOK.png", confidence=0.9, region=searchRegion)
            if findPos:
                pyautogui.click(findPos.x, findPos.y, duration=DURATION)
                # easy mode
                modeEasyX, modeEasyY = config['trainingGrounds']['modeeasy']
                logger.debug("游兵远征关卡选择简单模式")
                # 远征关卡模式需要点击2次才可以。
                pyautogui.moveTo(modeEasyX, modeEasyY, duration=DURATION*2)
                pyautogui.sleep(2)
                pyautogui.mouseDown()
                pyautogui.mouseUp()
                pyautogui.click(clicks=2, duration=DURATION)
                pyautogui.sleep(5)
                redDot = False

        # 一件扫荡功能
        oneKeyEanbled = pxlMatch(
            *config['trainingGrounds']['one_key_pos'], config['trainingGrounds']['one_key_rgb'], tolerance=10, comment="游兵远征-一件扫荡")
        # logger.debug("【游兵远征-一件扫荡】像素点颜色匹配结果:{},坐标{}，预期：{}，实际结果：{}".format(oneKeyEanbled, config['trainingGrounds']['one_key_pos'],
                                                                        #  config['trainingGrounds']['one_key_rgb'], pyautogui.pixel(*config['trainingGrounds']['one_key_pos'])))
        if oneKeyEanbled:
            pyautogui.click(config['trainingGrounds']
                            ['one_key_pos'], duration=DURATION)
            pyautogui.sleep(1)
            # 关闭等到奖励提示窗口
            pyautogui.click(config['trainingGrounds']
                            ['one_key_pos'], duration=DURATION)

        redDot = pxlMatch(
            *config['trainingGrounds']['chest1_reddot_pos'], config['Global']['red_dot'], tolerance=10, comment="游兵远征-是否有宝箱可以领取")

        if redDot:
            logger.debug("领取第一个随机神秘宝箱列表。")
            pyautogui.click(config['trainingGrounds']
                            ['chest1_reddot_pos'], duration=DURATION)
            pyautogui.click(config['trainingGrounds']
                            ['chest_pos'], duration=DURATION)
            # 关闭宝箱列表窗口
            pyautogui.click(config['trainingGrounds']
                            ['chest1_reddot_pos'], duration=DURATION)

            logger.debug("领取第二个随机神秘宝箱列表。")
            pyautogui.click(config['trainingGrounds']
                            ['chest2_reddot_pos'], duration=DURATION)
            pyautogui.click(config['trainingGrounds']
                            ['chest_pos'], duration=DURATION)
            # 关闭宝箱列表窗口
            pyautogui.click(config['trainingGrounds']
                            ['chest2_reddot_pos'], duration=DURATION)

            closeActiveWindow()
            return True

        
    closeActiveWindow()
    return False


def checkEmptyPosition():
    """检查是否战利品拍卖是否还有空位置"""
    logger.debug("检查是否战利品拍卖是否还有空位置。")
    # Merchant Guild 商人工会 商会坐标
    merchantGuildX, merchantGuildY = config['checkEmptyPosition']['merchantguild']
    pyautogui.click(merchantGuildX, merchantGuildY, duration=DURATION)

    # 检查是否还有剩余的战利品未拍卖
    searchRegion = config['checkEmptyPosition']['searchregion']
    findPos = pyautogui.locateCenterOnScreen(
        'scrShot-browser/emptySpoils.png', confidence=0.9, region=searchRegion)

    # pyautogui.sleep(2)
    closeActiveWindow()
    # 拍卖战利品的位置至少还有1个空余，可以派兵打箱子去咯。
    if findPos:
        logger.debug("战利品拍卖位置至少还有1个。")
        return True
    else:
        logger.debug("商会的战利品拍卖位置满了。")
        return False

    # """拍卖战利品 auction Spoils"""


def rallyAttack():
    # 集结攻击 叛军营地/巨龙巢穴
    # Rebel Camp  叛军营地
    # 检查有叛军营地的集结召集
    searchRegion = (SCREEN_MID_W, SCREEN_MID_H, SCREEN_MID_W, SCREEN_MID_H)
    findPos = pyautogui.locateCenterOnScreen(
        "scrShot-browser/rallyFlag.png", confidence=0.9, region=searchRegion)

    result = False
    # 点击召集的旗帜
    if findPos:
        logger.debug("有集结攻击召集的旗帜。")
        pyautogui.click(findPos.x, findPos.y, duration=DURATION)
        searchRegion = config['rallyAttack']['searchregion2']
        findPos = pyautogui.locateCenterOnScreen(
            "scrShot-browser/rebelCamp.png",  confidence=0.9, region=searchRegion)
        findPos2 = pyautogui.locateCenterOnScreen(
            "scrShot-browser/dragonhome.png",  confidence=0.9, region=searchRegion)
        findPos = findPos or findPos2
        if findPos:
            logger.debug("参加叛军营地集结，获取战利品宝箱。")
            # 弹出集结窗口
            pyautogui.click(findPos.x, findPos.y, duration=DURATION)
            # 参与集结
            btnJoinRallyX, btnJoinRallyY = config['rallyAttack']['btnjoinrally']
            pyautogui.click(btnJoinRallyX, btnJoinRallyY, duration=DURATION)

            # 确认参与集结 ，大概意思就是派出去的兵要听对方的了，不能调动。
            btnConfirmYesX, btnConfirmYesY = config['rallyAttack']['btnconfirmyes']
            pyautogui.click(btnConfirmYesX, btnConfirmYesY, duration=DURATION)

            # 弹出派兵的窗口
            result = sendTroops(arms="cavalry", num=500)
    else:
        logger.debug("无集结攻击旗帜。")

    return result


def attackRebel(level=15, elite=True):
    """
    攻击叛军据点卫士
    返回值：
        0， 表示查找一次就成功
        n>0, 表示查找n次就成功
        <0, 失败
    """
    logger.debug(f"攻击{level}级叛军据点，是否精英类型：{elite}。")
    # 搜索5次都不是精英叛军则放弃搜索，普通的搜索一次
    if elite:
        ret = searchSandBox("rebel", level, False)
        if not ret:
            return -1
        for finded in range(5):
            findElite = pxlMatch(
                *config["attackRebel"]["elite_pos"], config["attackRebel"]["elite_rgb"], tolerance=10, comment="精英叛军据点")
            if findElite:
                pyautogui.click(config["searchSandBox"]
                                ["btn_close"], duration=DURATION)
                break

            if finded == 4:
                logger.debug(f"经过{finded + 1}次查找，未匹配到精英叛军据点。")
                pyautogui.click(config["searchSandBox"]
                                ["btn_close"], duration=DURATION)
                return -1

            # 再次点击搜索
            pyautogui.click(config["searchSandBox"]
                            ["btn_search"], duration=DURATION)
    else:
        ret = searchSandBox("rebel", level, True)
        if not ret:
            return -1

    #
    pyautogui.click(SCREEN_MID_W, SCREEN_MID_H, duration=DURATION)

    # 攻击按钮 匹配颜色
    enable = pxlMatch(
        *config["attackRebel"]["btn_attack"], config["attackRebel"]["btn_attack_rgb"], tolerance=10, comment="精英叛军攻击按钮")

    if not enable:
        logger.debug("未匹配到精英叛军攻击按钮。")
        return -2

    # 行动力够不够？ 从进度条颜色还是读取数字
    startX, startY = config["attackRebel"]["progress_start"]
    stopX, stopY = config["attackRebel"]["progress_stop"]
    if startY != stopY:
        logger.error("配置文件中的起点坐标和终点坐标的Y轴必须一样。")
        return -3
    progress_value = config["attackRebel"]["progress_value"]
    lowest_motivation = config["attackRebel"]["lowest_motivation"]

    # 至少预留3次600的体力
    enoughX = round(lowest_motivation/progress_value *
                    (stopX - startX)) + 1 + startX
    enoughY = startY

    # 进度条是否未到目标位置
    enable_bg = pxlMatch(
        enoughX, enoughY, config["attackRebel"]["progress_bg_rgb"], tolerance=10, comment="精英叛军体力进度条")
    # logger.debug(f"*******{motivation*times}。point:{enoughX}, {enoughY}. rgb:{getrgb}")
    if enable_bg:
        logger.debug(f"攻打精英叛军体力不足，设置保留需要的体力力{lowest_motivation}。")
        closeActiveWindow()
        return -4

    pyautogui.click(config["attackRebel"]["btn_attack"], duration=DURATION)

    ret = sendTroops(arms="all", num=0, dragon=True, commander=6)
    if ret:
        return finded
    else:
        return -5


def attackRebelLeader(level=2, times=0):
    """
    攻击叛军野外首领
        level: 攻击野外首领级别
        times: 连续攻击野外首领次数
    返回值：

    """
    logger.debug(f"攻击{level}级野外首领。")

    # 查找野外首领
    searchSandBox("leader", level)

    pyautogui.click(SCREEN_MID_W, SCREEN_MID_H, duration=DURATION)
    # 攻击按钮 匹配颜色

    enable = pxlMatch(
        *config["attackRebelLeader"]["btn_max_attack"], config["attackRebelLeader"]["btn_max_attack_rgb"], tolerance=10, comment="叛军野外首领攻击按钮")

    if not enable:
        logger.debug("未匹配到叛军野外首领攻击按钮。")
        return False

    # 判断克制兵种，系统建议兵种
    recommended_troop_type = "icon_all"
    #  枪兵 先去
    for troop in ["icon_mauler", "icon_cavalry", "icon_spearman"]:
        findPosCenter = pyautogui.locateCenterOnScreen(
            f"scrShot-browser/{troop}.png", confidence=0.9, region=config["attackRebelLeader"]["search_region"])
        if findPosCenter:
            recommended_troop_type = troop
            break

    # 根据克制兵种，连续攻击次数
    # times = 3 if recommended_troop_type in ["", "icon_spearman","icon_all"] else 2

    # 行动力够不够？ 从进度条颜色还是读取数字
    startX, startY = config["attackRebelLeader"]["progress_start"]
    stopX, stopY = config["attackRebelLeader"]["progress_stop"]
    if startY != stopY:
        logger.error("配置文件中的起点坐标和终点坐标的Y轴必须一样。")
        return False
    progress_value = config["attackRebelLeader"]["progress_value"]
    motivation = config["attackRebelLeader"]["motivation"]
    enoughX = round(motivation*times/progress_value *
                    (stopX - startX)) + 1 + startX
    enoughY = startY
    # 进度条是否未到目标位置
    enable_bg = pxlMatch(
        enoughX, enoughY, config["attackRebelLeader"]["progress_bg_rgb"], tolerance=10, comment="叛军野外首领行动力")
    # logger.debug(f"*******{motivation*times}。point:{enoughX}, {enoughY}. rgb:{getrgb}")
    if enable_bg:
        logger.debug(f"攻打叛军野外首领行动力不足，需要的行动力{motivation*times}。")
        closeActiveWindow()
        return False

    pyautogui.click(config["attackRebelLeader"]
                    ["btn_max_attack"], duration=DURATION)

    # 选择输入连续攻击次数
    if times > 0:
        enable = pxlMatch(
            *config["attackRebelLeader"]["btn_attack"], config["attackRebelLeader"]["btn_attack_rgb"], tolerance=10, comment="叛军野外首领攻击次数")
        if not enable:
            return False
        pyautogui.click(config["attackRebelLeader"]
                        ["input_attack_times"], duration=DURATION)
        pyautogui.hotkey("ctrl", "A")

        pyautogui.write(str(times))
        pyautogui.click(config["attackRebelLeader"]
                        ["btn_attack"], duration=DURATION)
        pyautogui.sleep(3)

    logger.debug(f"克制兵种：{recommended_troop_type}，攻击次数：{times}。")

    # 选择上阵的指挥官
    pyautogui.click(config["attackRebelLeader"]
                    [recommended_troop_type], duration=DURATION)
    pyautogui.click(config["attackRebelLeader"]
                    ["btn_quick_formation"], duration=DURATION)

    # 枪兵指挥官不足,还需要:
    # if recommended_troop_type == "icon_spearman":
    #     pyautogui.click(config["attackRebelLeader"]["icon_all"], duration=DURATION )
    #     for i in range(3):
    #         card_baseX, card_baseY = config["attackRebelLeader"]["commander1"]
    #         pyautogui.click(card_baseX + i * config["attackRebelLeader"]["card_space"], card_baseY )

    pyautogui.click(config["attackRebelLeader"]
                    ["btn_battle"], duration=DURATION)

    # 判断是否有行军窗口信息
    pyautogui.sleep(2)
    enable = pxlMatch(
        *config["sendTroops"]["march_btn_back"], config["sendTroops"]["march_btn_back_rgb"], tolerance=10, comment="行军窗口信息")

    if not enable:
        return False
    logger.debug(f"攻击{level}级野外首领>>>指挥官军队派遣成功。")
    return True


def sendTroops(arms="all", num=0, dragon=False, commander=0, dispatch=0):
    """弹出派兵出征窗口，根据条件派遣相应兵力。
    参数：
        arms：all 所有能派的都派出去, mauler 盾兵, cavalry 骑兵, spearman 枪兵, bowman 弓兵
        num ： 派出兵种数量， =0 自动选择数量
        dragon: 是否需要派遣 龙 出征。
        commander: 上阵指挥官数量
        dispatch: 采集矿产派兵顺序 0 系统默认， 1 低阶优先合适兵量， 2高阶有限合适兵量， 3 低阶优先最大兵量，4 高阶优先最大兵量
    返回值：
        True: 派兵成功
        False： 派兵失败
    """
    pyautogui.sleep(2)
    # findPos = pyautogui.locateCenterOnScreen("scrShot-browser/sendTroops.png", confidence=0.9)
    findPos = pxlMatch(
        *config['sendTroops']['btn_ok'], config['sendTroops']['btn_ok_rgb'], tolerance=10, comment="派兵出征窗口")

    # 检查派兵出征窗口是否弹出
    if not findPos:
        logger.debug("未探测到派兵出征窗口，派遣失败。")
        return False

    arms_option = {
        "mauler": {"row": 0, "cursor": (0, 0), "input": (0, 0), "enable": False},
        "cavalry": {"row": 0, "cursor": (0, 0), "input": (0, 0), "enable": False},
        "spearman": {"row": 0, "cursor": (0, 0), "input": (0, 0), "enable": False},
        "bowman": {"row": 0, "cursor": (0, 0), "input": (0, 0), "enable": False}
    }
    arms_pos = {}
    find_none = 0

    # 输入派兵数量
    if num > 0 and arms != "all":
        # 查找每个兵种，匹配在第几行
        for img in ["mauler", "cavalry", "spearman", "bowman"]:
            findPos = pyautogui.locateCenterOnScreen(
                f"scrShot-browser/army/{img}.png", confidence=0.9)
            if findPos:
                arms_pos[img] = findPos[1]
            else:
                find_none += 1
                arms_pos[img] = 666666

        # 正常的4个兵种，只有一个是隐藏在下拉框下面所以匹配不到
        if find_none > 1:
            logger.error("派兵出征窗口，兵种位置匹配错误。")
            return False
        # 按照Y坐标排序
        arms_order = sorted(
            arms_pos.items(), key=lambda d: d[1], reverse=False)
        first_army = arms_order[0][0]
        i = 0
        for name, posY in arms_order:
            arms_option[name]["row"] = i
            arms_option[name]["cursor"] = config["sendTroops"][f"arm{i}_cursor"]
            arms_option[name]["input"] = config["sendTroops"][f"arm{i}_input"]

            if i == 3:
                pyautogui.scroll(-20)
            # 检查各个兵种是否可派出，就是判断兵种对应的滑动条是否可以移动
            pyautogui.click(arms_option[name]["cursor"])
            enable = pxlMatch(
                *arms_option[name]["cursor"], config['sendTroops']['cursor_focus_rgb'], tolerance=10, comment="兵种对应的滑动条")
            arms_option[name]["enable"] = enable
            if i == 3:
                pyautogui.scroll(20)

            logger.debug(f"第{i+1}行，兵种：{name}， 是否可用：{enable}，匹配图片Y坐标：{posY}")
            i += 1

        # 清空自动选择的兵力
        selected = pxlMatch(
            *config['sendTroops']['btn_arms_quick'], config['sendTroops']['btn_arms_quick_rgb'], tolerance=10, comment="清空自动选择的兵力")
        if selected:
            pyautogui.click(config['sendTroops']['btn_arms_quick'])

        # 如果目标兵种不可用，选择第一行的兵种
        if arms_option[arms]["enable"]:
            pyautogui.click(arms_option[arms]["input"])
        else:
            pyautogui.click(arms_option[first_army]["input"])

        pyautogui.hotkey("CTRL", "A")
        pyautogui.write(str(num))
    # else:
    #     pyautogui.click(config['sendTroops']['btn_arms_quick'])

    # 快速选择指挥官的按钮是否可用[绿色]
    ret = pxlMatch(
        *config['sendTroops']['quick_select'], config['sendTroops']['quick_select_rgb'], tolerance=10, comment="快速选择指挥官的按钮是否可用[绿色]")

    # 需要派指挥官上阵
    if commander > 0:
        if ret:
            pyautogui.click(config['sendTroops']
                            ['quick_select'], duration=DURATION)
    else:
        if not ret:
            pyautogui.click(config['sendTroops']
                            ['quick_select'], duration=DURATION)

    # 龙的位置是否空
    ret = pxlMatch(
        *config['sendTroops']['btnadddragon'], config['sendTroops']['add_dragon_rgb'], tolerance=10, comment="龙的位置是否空")
    # 是否需要派遣龙
    if dragon and ret:
        btnAddDragon = config['sendTroops']['btnadddragon']
        pyautogui.click(btnAddDragon, duration=DURATION)
        # 选择第一条龙
        DragonImage1 = config['sendTroops']['dragonimage1']
        pyautogui.click(DragonImage1, duration=DURATION)

        # 选择龙后 确认按钮 返回上层窗口
        dragonConfirm = config['sendTroops']['dragonconfirm']
        pyautogui.click(dragonConfirm, duration=DURATION)

    # 选择派兵顺序，在采集矿产的时候
    if dispatch == 1:
        pyautogui.click(config['sendTroops']
                        ['dispatch_step1'], duration=DURATION)
        pyautogui.click(config['sendTroops']
                        ['dispatch_step2'], duration=DURATION)
    pyautogui.click(config['sendTroops']['btn_ok'], duration=DURATION)
    pyautogui.sleep(2)  # 休息一会，行军窗口信息可能都没有弹出来。

    # 会出现一个行军信息窗口，判断是否派军成功【行军召回按钮】，然后需要关闭
    searchRegion = config['sendTroops']['searchregion2']
    findPosCenter = pyautogui.locateCenterOnScreen(
        'scrShot-browser/march.png', confidence=0.9, region=searchRegion)
    if findPosCenter:
        march_window = config['sendTroops']['march_window']
        pyautogui.click(march_window, duration=DURATION)
        # closeActiveWindow()
        logger.debug("军队派遣成功。")
        return True  # 军队派出成功
    else:
        logger.debug("军队派遣状态未知，未探测到行军窗口信息。")
        closeActiveWindow()
        return False


def openCityAdminWindow(function=1):
    """
    城市管理界面窗口
    function        
        1: 联盟BOSS扫荡        
        2: 战利品拍卖
        3: 叛军据点扫荡
        4: 心树扫荡
        5: 演武场扫荡    
    """
    if function == 1:
        logger.debug("内政管理模式，点击【联盟BOSS扫荡】的立即完成按钮。")
        btnCompeleteX, btnCompeleteY = config['openCityAdminWindow']['btncompelete1']
    elif function == 2:
        logger.debug("内政管理模式，点击【战利品拍卖】的立即完成按钮。")
        btnCompeleteX, btnCompeleteY = config['openCityAdminWindow']['btncompelete2']
    elif function == 3:
        logger.debug("内政管理模式，点击【叛军据点扫荡】的立即完成按钮。")
        btnCompeleteX, btnCompeleteY = config['openCityAdminWindow']['btncompelete3']
    elif function == 4:
        logger.debug("内政管理模式，点击【心树扫荡】的立即完成按钮。")
        btnCompeleteX, btnCompeleteY = config['openCityAdminWindow']['btncompelete4']
    elif function == 5:
        logger.debug("内政管理模式，点击【演武场扫荡】的立即完成按钮。")
        btnCompeleteX, btnCompeleteY = config['openCityAdminWindow']['btncompelete5']
    elif function == 6:
        logger.debug("内政管理模式，点击【酒馆奖励】的立即完成按钮。")
        btnCompeleteX, btnCompeleteY = config['openCityAdminWindow']['btncompelete6']
    else:
        logger.error("城市管理界面错误的功能选项参数")
        return False

    # 内政管理模式按钮，可以快速完成几个大功能。
    cityAdminX, cityAdminY = config['openCityAdminWindow']['cityadmin']
    pyautogui.click(cityAdminX, cityAdminY, duration=DURATION)
    pyautogui.sleep(1)

    pyautogui.click(btnCompeleteX, btnCompeleteY, duration=DURATION)
    pyautogui.sleep(1)

    # 检查是否执行成功
    ret = False
    if function == 1:
        ret = pxlMatch(
            *config['openCityAdminWindow']['compeleted1_pos'], config['openCityAdminWindow']['compeleted1_rgb'], tolerance=10, comment="政务处理-联盟BOSS扫荡")
        # logger.debug("【政务处理-联盟BOSS扫荡】像素点颜色匹配结果:{},坐标{}，预期：{}，实际结果：{}".format(ret, config['openCityAdminWindow']['compeleted1_pos'],
        #                                                                      config['openCityAdminWindow']['compeleted1_rgb'], pyautogui.pixel(*config['openCityAdminWindow']['compeleted1_pos'])))
    elif function == 2:
        ret = pxlMatch(
            *config['openCityAdminWindow']['btncompelete2'], config['openCityAdminWindow']['compelete2_rgb'], tolerance=10, comment="政务处理-联盟BOSS扫荡")
        # logger.debug("【政务处理-联盟BOSS扫荡】像素点颜色匹配结果:{},坐标{}，预期：{}，实际结果：{}".format(ret, config['openCityAdminWindow']['btncompelete2'],
        #                                                                      config['openCityAdminWindow']['compelete2_rgb'], pyautogui.pixel(*config['openCityAdminWindow']['btncompelete2'])))

    # Close the window
    closeActiveWindow()

    return ret


def getText(textRegion, lang="eng"):
    """OCR image text"""
    OCR_Img = pyautogui.screenshot(region=textRegion)
    OCR_Text = pytesseract.image_to_string(OCR_Img, lang=lang)
    logger.debug("Tesseract 识别屏幕区域【{}】的图片文字是【{}】".format(textRegion, OCR_Text))
    return OCR_Text


def pareseCoordinates(text):
    ''''''
    if not text:
        return None
    logger.debug("开始解析字符串中的坐标数据，传入的字符：{}".format(text))
    # 默认为所有的空字符，包括空格、换行(\n)、制表符(\t)等
    wordList = text.split()
    if len(wordList) == 2:
        strX = wordList[0].replace("X:", "")
        strY = wordList[1].replace("Y:", "")
        logger.debug("根据传入的字符串解析出的坐标值 X={}, Y={}".format(strX, strY))
        if not strX.isnumeric() or not strY.isnumeric():
            logger.debug("非数字字符串，不能转换为数值。")
            return None
        return (int(strX), int(strY))
    else:
        logger.debug("解析坐标字符串失败。")
        return None


def permit(pos):
    """根据坐标判断是否允许采集区域，该坐标是游戏内坐标。"""
    # 开关关闭代表所有区域可以采集
    if not config["permit"]["on_off"]:
        return True

    if not pos:
        logger.error("传入采集矿产坐标参数错误, pos= %s", pos)
        return None
    if not isinstance(pos, tuple):
        logger.error("传入采集矿产坐标参数不是元组类型, pos= %s", pos)
        return None
    if len(pos) != 2:
        logger.error("传入采集矿产坐标参数长度应为2， pos= %s", pos)
        return None

    for k, v in config["permit"].items():
        if k.find("restricted_area") == 0:
            beginX, beginY, endX, endY = v
            posX, posY = pos
            if posX > beginX and posX < endX and posY > beginY and posY < endY:
                logger.debug(f"当前的坐标{pos}在限制区域内{v}。")
                return False

    return True


def getArmyNum():
    """获取派遣军队数量"""
    # textRegion=(260,790,30,40)
    # searchRegion=(1100, 200, 400, 700)
    # armyFlagX, armyFlagY=41,797
    # pyautogui.click(armyFlagX, armyFlagY, duration=DURATION)
    # pyautogui.sleep(2)
    # findList = list(pyautogui.locateAllOnScreen('scrShot-browser/armyGo.png', region=searchRegion))
    # windowCloseX, windowCloseY = 1467, 210
    # pyautogui.click(windowCloseX,windowCloseY, duration=DURATION)
    # # 找到多个的话就拍卖第一个
    # num = len(findList)

    # 第二种方法获取派遣军队的数量
    searchRegion = config["getArmyNum"]["searchregion"]
    num = 0
    for i, f in enumerate(('scrShot/one.png', 'scrShot/two.png', 'scrShot/three.png', 'scrShot/four.png', 'scrShot/five.png', 'scrShot/six.png')):
        im = pyautogui.locateOnScreen(f, confidence=0.9, region=searchRegion)
        if im:
            num = i + 1
            break

    logger.debug("获取派遣军队的队列数量。== {}".format(num))
    return num


def searchSandBox(resType="mine", level=3, close=True):
    '''
    使用界面上面的沙盘的搜索功能，搜索相应的资源类型
    参数：
        resType: ["farm"农田,"mine"铁矿,"mint"铸币厂,"quarry"采石场,"lumberyard"木材厂，"rebel"叛军据点, "leader"野外首领]
        level: [1-25]
        close: 搜索完是否立即关闭窗口
    返回值：
        True 搜索相应的资源成功
        False
    '''
    # 打开搜索窗口
    pyautogui.click(config["searchSandBox"]["icon_search"], duration=DURATION)

    # 判断搜索窗口是否打开
    ret = pxlMatch(
        *config['searchSandBox']['btn_search'], config['searchSandBox']['btn_search_rgb'], tolerance=10, comment="搜索窗口是否打开")
    if not ret:
        logger.error("沙盘搜索窗口未打开。")
        return False

    farmLeftTopX, farmLeftTopY = config["searchSandBox"]["base"]
    offsetX, offsetY = config["searchSandBox"]["offset"]
    searchTypePoint = {
        "farm": (farmLeftTopX, farmLeftTopY),  # 农田  R1C1
        "mint": (farmLeftTopX + offsetX*1, farmLeftTopY),  # 铸币厂R1C2
        "quarry": (farmLeftTopX + offsetX*2, farmLeftTopY),  # 采石场R1C3
        "lumberyard": (farmLeftTopX, farmLeftTopY + offsetY*1),  # 木材厂R2C1
        "mine": (farmLeftTopX + offsetX*1, farmLeftTopY + offsetY*1),  # 铁矿R2C2
        "camp": (farmLeftTopX + offsetX*2, farmLeftTopY + offsetY*1),  # 叛军营地R2C3
        # 野外叛军首领R3C2
        "leader": (farmLeftTopX + offsetX*1, farmLeftTopY + offsetY*2),
        "rebel": (farmLeftTopX, farmLeftTopY + offsetY*2)  # 叛军据点 R3C1
    }

    if resType == "rebel":
        totalLevel = 25
    else:
        totalLevel = 5

    # 其他资源类型都是划分为5级
    # 进度条长度是300 pix， startX=343，startY=772
    startX, startY = config["searchSandBox"]["progress_start"]
    stopX, stopY = config["searchSandBox"]["progress_stop"]

    searchLevelX = startX + (stopX - startX)/totalLevel * level
    if startY == stopY:
        searchLevelY = startY
    else:
        logger.error("搜索沙盘窗口级别进度条起点和终点Y座标配置错误，必须一致。")
        return False

    logger.debug("沙盘搜索资源，资源类型:{}, 资源级别:{}".format(resType, level))

    # 点击资源类型
    pyautogui.click(*searchTypePoint[resType], duration=DURATION)

    # 点击资源级别
    pyautogui.click(searchLevelX, searchLevelY, duration=DURATION)

    # 开始搜索
    pyautogui.click(config["searchSandBox"]["btn_search"], duration=DURATION)

    # 点击搜索到的资源,搜到的资源会到屏幕的中央
    # pyautogui.click(SCREEN_MID_W, SCREEN_MID_H, duration=DURATION)

    # close the search window
    if close:
        pyautogui.click(config["searchSandBox"]
                        ["btn_close"], duration=DURATION)

    return True


def claimChest(map_mode="city"):
    """例行检查神秘宝箱，并领取"""

    if map_mode != "city" and map_mode != "world":
        return False
    if map_mode == "city":
        redDotX, redDotY = config['claimChest']['red_dot_city']
        redDotX2, redDotY2 = config['claimChest']['red_dot_city2']

    if map_mode == "world":
        redDotX, redDotY = config['claimChest']['red_dot_world']
        redDotX2, redDotY2 = config['claimChest']['red_dot_world2']

    redDot = pxlMatch(
        redDotX, redDotY, config['claimChest']['red_dot_rgb'], tolerance=20, comment="神秘宝箱红点1")
    redDot2 = pxlMatch(
        redDotX2, redDotY2, config['claimChest']['red_dot_rgb'], tolerance=20, comment="神秘宝箱红点2")

    redDot = redDot or redDot2

    result = False
    enable = False
    if redDot:
        pyautogui.press("Q")
        enable = pxlMatch(
            *config["claimChest"]["btn_claim"], config['claimChest']['btn_claim_rgb'], tolerance=10, comment="神秘宝箱领取按钮")
        if enable:
            pyautogui.click(config["claimChest"]
                            ["btn_claim"], duration=DURATION)
            result = True
        else:
            result = False
    redDotRGB = pyautogui.pixel(redDotX, redDotY)
    logger.debug(
        f"例行检查神秘宝箱，地图={map_mode}，是否红点={redDot},是否领取成功={enable}。X={redDotX}, Y={redDotY},RGB={redDotRGB}")
    return result


def challengeAlliance():
    """联盟试炼，挑战过关"""
    # 联盟快捷键
    logger.debug("每日任务>>>联盟试炼挑战任务。")
    pyautogui.press('c')
    challengeIcon = config['challengeAlliance']['challengeicon']
    pyautogui.click(challengeIcon, duration=DURATION)
    pyautogui.sleep(2)

    # 界面上只有6个指挥官位置
    for i in range(6):
        name = 'commander{}'.format(i+1)
        commander = config['challengeAlliance'][name]
        # 目前总共24名指挥官，每次出征6名, 共4批次
        batch = 0
        while True or batch < 5:
            # 检查试炼道路 是哪一个指挥官。
            pyautogui.click(commander, duration=DURATION)
            searchRegion = (SCREEN_MID_W, SCREEN_MID_H,
                            SCREEN_MID_W, SCREEN_MID_H)
            findPosCenter = pyautogui.locateCenterOnScreen(
                'scrShot-browser/challenge.png', confidence=0.9, region=searchRegion)
            if findPosCenter:
                batch += 1
                logger.debug("已经找到联盟试炼比拼指挥官 {}, 坐标 {}。".format(
                    name, findPosCenter))
                pyautogui.click(findPosCenter, duration=DURATION)
                pyautogui.sleep(4)

                # 一键布阵
                pyautogui.click(config['challengeAlliance']
                                ['one_key_array'], duration=DURATION)
                searchRegion = (0, 0, SCREEN_MID_W, SCREEN_HEIGHT)
                # 上阵几名指挥官，至少也要1名，否则判定为无指挥官可以派遣
                find = pyautogui.locateCenterOnScreen(
                    'scrShot-browser/emptyCommander.png', confidence=0.9, region=searchRegion)

                # 6个空位置，表明无指挥官可以派遣
                if find:
                    logger.debug("联盟试炼比拼,无指挥官可以派遣。")
                    break
                    # closeActiveWindow()
                    # return True

                logger.debug("联盟试炼，第{}批次，派遣指挥官出征。".format(batch))
                # 开战
                pyautogui.click(config['challengeAlliance']
                                ['button_battle'], duration=DURATION)
                pyautogui.sleep(3)

                # 退出
                pyautogui.click(config['challengeAlliance']
                                ['button_quit'], duration=DURATION)
                pyautogui.click(commander, duration=DURATION)
            else:
                pyautogui.click(config['challengeAlliance']
                                ['cmder_wnd_close'], duration=DURATION)
                logger.debug("还未找到联盟试炼比拼指挥官 {}， 查询下一个。".format(name))
                break

    closeActiveWindow()
    return True


def claimAlliance():
    """# 收集联盟的礼物"""

    # 联盟快捷键
    logger.debug("例行检查检查联盟礼物。")
    pyautogui.press('C')

    pyautogui.click(config["claimAlliance"]["cell_gift"], duration=DURATION)
    pyautogui.click(config["claimAlliance"]["gift_all"], duration=DURATION)
    # 有时候领取礼物后弹出窗口，会影响删除按钮区域，多点击1次
    pyautogui.click(config["claimAlliance"]["gift_del"],
                    clicks=2, interval=2, duration=DURATION)
    closeActiveWindow()

    return True

    # logger.debug("例行检查检查联盟帮助。")
    # pyautogui.press('c')
    # allianceHelpWndX, allianceHelpWndY = 1377, 652
    # pyautogui.click(allianceHelpWndX, allianceHelpWndY, duration=DURATION)

    # buttonHelpX, buttonHelpY=936,856
    # pyautogui.click(buttonHelpX, buttonHelpY, duration=DURATION)
    # closeActiveWindow()


def helpAlliance():
    """联盟帮助点击"""
    # #找到联盟帮助的图标
    searchRegion = config['helpAlliance']['searchregion']
    findPosCenter = pyautogui.locateCenterOnScreen(
        'scrShot/alliancehelp.png', confidence=0.9, region=searchRegion)
    if findPosCenter:
        logger.debug("找到联盟帮助图标，点击帮助。")
        pyautogui.click(findPosCenter, duration=DURATION)
        return True
    # else:
    #     logger.debug("未找到联盟帮助图标。")
    return False


def checkQuest(mode="alliance"):
    """
    检查任务列表
      参数 mode: 
            alliance 联盟任务
            vip      VIP任务宝箱
    """
    # 检查任务列表 红点不会自动出现，需要手工点击刷新出来
    logger.debug("开始检查所有任务列表。。。。。。")

    # 打开任务列表窗口
    # pyautogui.click(findPos.left + offsetX, findPos.top + offsetY, duration=DURATION)
    pyautogui.press("z")
    pyautogui.sleep(2)
    result = False

    if mode == "alliance":
        # 联盟任务列表
        logMsg = "检查联盟任务列表：还不能领取。"
        pyautogui.click(config['checkQuest']
                        ['alliancetask'], duration=DURATION)
        enable = pxlMatch(
            *config['checkQuest']['alliancetask_claim'], config['checkQuest']['claim_enable_rgb'], tolerance=10, comment="联盟任务列表领取按钮")
        if enable:
            pyautogui.click(config['checkQuest']
                            ['alliancetask_claim'], duration=DURATION)
            result = True
            logMsg = "检查联盟任务列表：领取成功。"
        logger.debug(logMsg)

    # VIP任务列表
    if mode == "vip":
        logMsg = "检查VIP任务奖励宝箱：还不能领取。"
        pyautogui.click(config['checkQuest']['viptask'], duration=DURATION)
        # 搜索哪个宝箱未打开 confidence=0.9,
        searchRegion = config['checkQuest']['searchregion']
        findpos = pyautogui.locateCenterOnScreen(
            'scrShot/vipbox.png',  region=searchRegion, confidence=0.9)
        if findpos:
            logMsg = "检查VIP任务奖励宝箱：领取VIP宝箱成功。x={},y={}".format(
                findpos.x, findpos.y)
            pyautogui.click(findpos.x, findpos.y, duration=DURATION)
            result = True

        logger.debug(logMsg)

    # 关闭任务列表窗口
    closeActiveWindow()
    # logger.debug("结束检查所有任务列表。。。。。。")

    return result


def claimSalary():
    '''# 领取俸禄'''
    logger.debug("每日任务>>>领取官爵每日俸禄。")
    # 右上角领取俸禄图标位置
    # NobilityWndX, NobilityWndY = 412, 104
    pyautogui.moveTo(config['claimSalary']['nobilitywnd'], duration=DURATION)
    pyautogui.sleep(1)
    pyautogui.click(config['claimSalary']['nobilitywnd'], duration=DURATION)
    pyautogui.sleep(1)
    # pyautogui.click(NobilityWndX, NobilityWndY, duration=DURATION)

    # btnClaimX, btnClaimY = 1301, 341
    pyautogui.click(config['claimSalary']['btnclaim'], duration=DURATION)
    # 检查是否领取成功
    claimed = pxlMatch(
        *config['claimSalary']['claimed_pos'], config['claimSalary']['claimed_rgb'], tolerance=10, comment="领取俸禄")

    closeActiveWindow()

    return claimed


def getVIPPoints():
    '''# 领取VIP'''
    logger.debug("每日任务>>>领取VIP每日积分，每日免费宝箱。")
    # VIP积分领取入口
    # VIPX, VIPY = 491, 100
    pyautogui.click(config['getVIPPoints']['vip'], duration=DURATION)
    # VIP 每日积分
    # VIPFreeChestX, VIPFreeChestY = 1388, 328
    pyautogui.click(config['getVIPPoints']
                    ['vipdailypoints'], duration=DURATION)
    points_claimed = pxlMatch(
        *config['getVIPPoints']['points_pos'], config['getVIPPoints']['points_claimed_rgb'], tolerance=10, comment="每日任务>>>领取VIP每日积分")

    # logger.debug("【每日任务>>>领取VIP每日积分】像素点颜色匹配结果:{},坐标{}，预期：{}，实际结果：{}".format(points_claimed, config['getVIPPoints']['points_pos'],
    #                                                                         config['getVIPPoints']['points_claimed_rgb'], pyautogui.pixel(*config['getVIPPoints']['points_pos'])))

    # VIP 免费宝箱
    # VIPDailyChestX, VIPDailyChestY = 1320, 627
    # 前面会弹出一个提示框，本处按钮多点击2次
    pyautogui.click(config['getVIPPoints']['vipfreechest'],
                    clicks=2, interval=2, duration=DURATION)
    chest_claimed = pxlMatch(
        *config['getVIPPoints']['chest_pos'], config['getVIPPoints']['chest_claimed_rgb'], tolerance=10, comment="每日任务>>>领取VIP免费宝箱")
    # logger.debug("【每日任务>>>领取VIP免费宝箱】像素点颜色匹配结果:{},坐标{}，预期：{}，实际结果：{}".format(chest_claimed, config['getVIPPoints']['chest_pos'],
    #                                                                         config['getVIPPoints']['chest_claimed_rgb'], pyautogui.pixel(*config['getVIPPoints']['chest_pos'])))

    closeActiveWindow()

    return (points_claimed and chest_claimed)


def getCOVID_bonus():
    '''领取疫情期间的奖励'''
    logger.debug("每日任务>>>领取COVID-19疫情期间每日的奖励。")
    # 疫情奖励入口
    # COVIDX, COVIDY = 402, 172
    pyautogui.click(config['getCOVID_bonus']['covid'], duration=DURATION)

    # 领取奖励按钮
    # btnClaimX, btnClaimY = 1358, 778
    pyautogui.click(config['getCOVID_bonus']['btnclaim'], duration=DURATION)
    # 检查是否领取成功
    claimed = pxlMatch(
        *config['getCOVID_bonus']['claimed_pos'], config['getCOVID_bonus']['claimed_rgb'], tolerance=10, comment="每日任务>>>领取疫情每日奖励")

    closeActiveWindow()

    return claimed


def getChestOfWelfareCenter():
    """领取福利中心给的宝箱，每天早上北京时间9点以后重置，可以领取。"""

    # WelfareCenterX, WelfareCenterY = 1448, 78
    # 福利中心入口图标搜索区域
    searchRegion = config['getChestOfWelfareCenter']['searchregion']
    findPosCenter = pyautogui.locateCenterOnScreen(
        'scrShot-browser/welfareCenter.png', confidence=0.9, region=searchRegion)
    if findPosCenter:
        logger.debug("进入福利中心，领取相关免费福利。")
        pyautogui.click(findPosCenter, duration=DURATION*3)
        pyautogui.sleep(2)
    else:
        logger.debug("未找到福利中心入口。")
        return False

    findVipShop = False
    findSpecialGiftPack = False
    findDragonGift = False  # 龙印礼包
    count = 0
    ret = False
    while True:
        if findVipShop == False:
            findPosCenter = pyautogui.locateCenterOnScreen(
                'scrShot-browser/VipShop.png', confidence=0.9)
            if findPosCenter:
                logger.debug("每日任务>>>领取福利中心-VIP商店的免费宝箱，")
                pyautogui.click(findPosCenter, duration=DURATION)
                # 免费宝箱
                pyautogui.click(
                    config['getChestOfWelfareCenter']['chest'], duration=DURATION)
                findVipShop = True
        # 紫晶商店
        if findSpecialGiftPack == False:
            findPosCenter = pyautogui.locateCenterOnScreen(
                'scrShot-browser/specialGiftPack.png', confidence=0.9)
            if findPosCenter:
                logger.debug("每日任务>>>领取福利中心-紫晶商店的免费宝箱，")
                pyautogui.click(findPosCenter, duration=DURATION)
                # 免费宝箱
                pyautogui.click(
                    config['getChestOfWelfareCenter']['chest2'], duration=DURATION)
                findSpecialGiftPack = True
                # 购买1黑钻礼包，用于购买赛格巨人
                pyautogui.click(798, 772, duration=DURATION)
                pyautogui.click(1143, 673, duration=DURATION)

        # 龙印礼包
        if findDragonGift == False:
            findPosCenter = pyautogui.locateCenterOnScreen(
                'scrShot-browser/dragon_gift.png', confidence=0.9)
            if findPosCenter:
                logger.debug("每日任务>>>领取福利中心-龙印礼包的免费宝箱，")
                pyautogui.click(findPosCenter, duration=DURATION)
                # 免费宝箱
                pyautogui.click(
                    config['getChestOfWelfareCenter']['chest2'], duration=DURATION)
                findDragonGift = True

        if findVipShop and findSpecialGiftPack and findDragonGift:
            ret = True
            break
        else:
            # 福利中心左侧共能区域 一个坐标，方便鼠标滚轮影响区域。
            pyautogui.moveTo(config['getChestOfWelfareCenter']
                             ['vipshop'], duration=DURATION)
            pyautogui.scroll(-2)

        # 超时退出
        count += 1
        if count > config['getChestOfWelfareCenter']['times_out']:
            logger.debug("每日任务>>>领取福利中心-免费的宝箱，超时退出")
            ret = False
            break

    closeActiveWindow()
    return ret


def dragFunc(mode="explore"):
    """
    龙界面的一些功能
        talentB+R: 龙天赋技能 建造加速，研发加速等。每天一次
        talentB: 龙1天赋技能 建造加速。每天一次
        talentR: 龙1天赋技能 研发加速。每天一次
        talent2G: 龙2天赋技能 快速采集
        raffle: 龙技能碎片抽取
        explore: 龙游历
    """

    pyautogui.press("D")
    pyautogui.sleep(3)
    result = False

    # 龙游历
    if mode == "explore":
        pyautogui.click(config['dragFunc']['dragon1'], duration=DURATION)
        pyautogui.sleep(2)
        logger.debug("自动龙游历获取成长经验的奖励。")

        # 领取游历完成的奖励物品
        pyautogui.click(config['dragFunc']['claimbonus'], duration=DURATION)
        # 龙游历按钮，如果是有奖励收集，则现点击奖励收集
        pyautogui.click(config['dragFunc']['exploring'], duration=DURATION)
        # 领取游历完成的奖励物品
        pyautogui.click(config['dragFunc']['claimbonus'], duration=DURATION)

        # 如果提示相片框满了，点击取消删除
        # pyautogui.click(config['dragFunc']['btncancel'], duration=DURATION)

        pyautogui.click(config['dragFunc']['exploring'], duration=DURATION)
        # 获取游历的时长
        explore_duration = config['dragFunc']['explore_duration']
        btngo = "btngo_{}h".format(explore_duration)
        enable = pxlMatch(
            *config['dragFunc'][btngo], config['dragFunc']['btngo_enable_rgb'], tolerance=10, comment="自动龙游历{}小时按钮".format(explore_duration))
        if enable:
            # 龙游历3小时出发
            pyautogui.click(config['dragFunc'][btngo], duration=DURATION)
            result = True

    # raffle: 龙技能碎片抽取
    if mode == "raffle":
        logger.debug("自动抽取每日5次的龙技能。")
        pyautogui.click(config['dragFunc']['skill'], duration=DURATION)
        findPosCenter = pyautogui.locateCenterOnScreen(
            'scrShot-browser/getDragSkill.png', confidence=0.9)
        if findPosCenter:
            logger.debug("匹配找到有免费的龙技能可以抽取一次。")
            pyautogui.click(findPosCenter, duration=DURATION)
            result = True
        else:
            logger.debug("免费的龙技能暂时还不能抽取。")

    # talent: 龙天赋技能 建造加速+研发加速。每天一次
    if mode == "talentB+R":
        result1 = False
        result2 = False

        pyautogui.click(config['dragFunc']['dragon1'], duration=DURATION)
        pyautogui.sleep(4)

        enable_construction = pxlMatch(
            *config['dragFunc']['btnconstructionspeed'], config['dragFunc']['construction_enable_rgb'], tolerance=10, comment="龙天赋技能 建造加速")

        enable_research = pxlMatch(
            *config['dragFunc']['btnresearchspeed'], config['dragFunc']['research_enable_rgb'], tolerance=10, comment="龙天赋技能 研发加速")

        # 建造加速
        if enable_construction:
            pyautogui.click(config['dragFunc']['btnconstructionspeed'])
            result1 = True
            logger.debug("使用龙1天赋【建造加速】成功。")

        # 研发加速
        if enable_research:
            pyautogui.click(config['dragFunc']['btnresearchspeed'])
            logger.debug("使用龙1天赋【研发加速】成功。")
            result2 = True

        result = result1 and result2

    # talent: 龙天赋技能 建造加速。每天一次
    if mode == "talentB":
        logger.debug("使用龙1天赋【建造加速】......")
        pyautogui.click(config['dragFunc']['dragon1'], duration=DURATION)
        pyautogui.sleep(4)

        enable_construction = pxlMatch(
            *config['dragFunc']['btnconstructionspeed'], config['dragFunc']['construction_enable_rgb'], tolerance=10, comment="使用龙1天赋 建造加速")

        # 建造加速
        if enable_construction:
            pyautogui.click(config['dragFunc']
                            ['btnconstructionspeed'], duration=DURATION)
            enable_construction = pxlMatch(
                *config['dragFunc']['btnconstructionspeed'], config['dragFunc']['construction_enable_rgb'], tolerance=10, comment="使用龙1天赋 建造加速")
            if not enable_construction:
                result = True
                logger.debug("使用龙1天赋【建造加速】成功。")

    # talent: 龙天赋技能+研发加速。每天一次
    if mode == "talentR":
        logger.debug("使用龙1天赋【研发加速】......")
        pyautogui.click(config['dragFunc']['dragon1'], duration=DURATION)
        pyautogui.sleep(4)

        enable_research = pxlMatch(
            *config['dragFunc']['btnresearchspeed'], config['dragFunc']['research_enable_rgb'], tolerance=10, comment="使用龙1天赋 研发加速")

        # 研发加速
        if enable_research:
            pyautogui.click(config['dragFunc']
                            ['btnresearchspeed'], duration=DURATION)
            enable_research = pxlMatch(
                *config['dragFunc']['btnresearchspeed'], config['dragFunc']['research_enable_rgb'], tolerance=10, comment="使用龙1天赋 研发加速")
            if not enable_research:
                result = True
                logger.debug("使用龙1天赋【研发加速】成功。")

    # talent: 龙天赋技能 快速采集。每天一次
    if mode == "talent2G":
        pyautogui.moveTo(config['dragFunc']['dragon2'], duration=DURATION)
        pyautogui.click(config['dragFunc']['dragon2'], duration=DURATION)
        pyautogui.click(config['dragFunc']['dragon2'], duration=DURATION)
        # pyautogui.sleep(2)

        enable_quick_gather = pxlMatch(
            *config['dragFunc']['quick_gather'], config['dragFunc']['quick_gather_enable_rgb'], tolerance=10, comment="使用龙2天赋 快速采集")

        # 快速采集
        if enable_quick_gather:
            pyautogui.click(config['dragFunc']['quick_gather'])
            logger.debug("使用龙2天赋【快速采集】成功。")
            result = True

    closeActiveWindow()
    return result


def trainSoldiers(arms, num=544, level=3):
    """训练士兵，
    参数：
        arms 兵种，mauler 盾兵, cavalry 骑兵, bowman 弓兵, spearman 枪兵
        num 士兵数量
        level 训练多少级别的兵种。    
    """
    logger.debug("检查兵营是否正在进行士兵训练。")
    barrackX, barrackY = config['trainSoldiers']['barrack']
    pyautogui.click(barrackX, barrackY, duration=DURATION)
    pyautogui.sleep(4)

    tabTrainningX, tabTrainningY = config['trainSoldiers']['tabtrainning']
    pyautogui.click(tabTrainningX, tabTrainningY, duration=DURATION)

    # 检查是否已经正在训练士兵了。
    searchRegion = (SCREEN_MID_W, SCREEN_MID_H, SCREEN_MID_W, SCREEN_MID_H)
    findPosCenter = pyautogui.locateCenterOnScreen(
        'scrShot-browser/speedup.png', confidence=0.9, region=searchRegion)
    if findPosCenter:
        logger.debug("已经有正在训练士兵，退出界面。")
        closeActiveWindow()
        return False

    logger.debug(
        "兵营中暂无士兵训练，计划本批次训练士兵， 兵种：{} 级别：{} 数量：{}".format(arms, level, num))

    soliderLevel1X, soliderLevel1Y = config['trainSoldiers']['soliderlevel1']
    soliderLevel2X, soliderLevel2Y = config['trainSoldiers']['soliderlevel2']
    soliderLevel3X, soliderLevel3Y = config['trainSoldiers']['soliderlevel3']
    soliderLevel4X, soliderLevel4Y = config['trainSoldiers']['soliderlevel4']

    if level == 1:
        levelX, levelY = soliderLevel1X, soliderLevel1Y
    elif level == 2:
        levelX, levelY = soliderLevel2X, soliderLevel2Y
    elif level == 3:
        levelX, levelY = soliderLevel3X, soliderLevel3Y
    elif level == 4:
        levelX, levelY = soliderLevel4X, soliderLevel4Y
    else:
        levelX, levelY = 0, 0

    pyautogui.click(levelX, levelY, duration=DURATION)

    pyautogui.click(config['trainSoldiers'][arms], duration=DURATION)

    inputNumX, inputNumY = config['trainSoldiers']['inputnum']
    pyautogui.click(inputNumX, inputNumY, duration=DURATION)
    pyautogui.hotkey("CTRL", "A")
    pyautogui.write(str(num))

    btnTrainX, btnTrainY = config['trainSoldiers']['btntrain']
    pyautogui.click(btnTrainX, btnTrainY, duration=DURATION)

    # 快速使用资源道具确认信息窗口
    btnConfirmX, btnConfirmY = config['trainSoldiers']['btnconfirm']
    pyautogui.click(btnConfirmX, btnConfirmY, duration=DURATION)
    pyautogui.sleep(2)

    findPosCenter = pyautogui.locateCenterOnScreen(
        'scrShot-browser/speedup.png', confidence=0.9, region=searchRegion)
    closeActiveWindow()
    if findPosCenter:
        logger.debug("本批次训练士兵成功。")
        return True
    else:
        logger.debug("本批次训练士兵失败。")
        return False


def sendMessage(msg=".", channel="world"):
    """在世界频道发送一条消息，完成当日任务。"""
    logger.debug("每日任务>>>在世界频道发送一条消息。")
    # 打开通讯录界面
    pyautogui.press("F")
    pyautogui.sleep(4)

    # 通讯录 世界频道
    # worldChannelX, worldChannelY = 1647, 313
    pyautogui.click(config['sendMessage']['worldchannel'], duration=DURATION)

    # 发送消息输入框
    # inputTextX, inputTextY = 725, 846
    pyautogui.click(config['sendMessage']['inputtext'], duration=DURATION)

    #
    pyautogui.write(msg, interval=0.25)

    # 消息发送按钮
    # btnSendX, btnSendY = 1267, 848
    pyautogui.click(config['sendMessage']['btnsend'], duration=DURATION)

    # 检查消息是否发送成功

    findPosCenter = pyautogui.locateCenterOnScreen(
        "scrShot-browser/headImageMsg.png", region=config['sendMessage']['searchregion'], confidence=0.9)
    sendSuccess = True if findPosCenter else False

    closeActiveWindow()

    return sendSuccess


def doCommanderFunc(mode="card", row=1, col=1):
    """
    执行指挥官的一些功能
        mode: 
            card,  获取指挥官的碎片
            exp ， 升级指挥经验
        row： 指挥官行位置
        col： 指挥官列位置
    """
    # 打开指挥官界面
    pyautogui.press("H")
    # 指挥官列表窗口弹出有些慢，等待会
    pyautogui.sleep(4)
    result = False

    

    if mode == "card":
        logger.debug("选择指挥官行：{}列：{}。".format(row, col))

        row -= 1
        col -= 1

        pyautogui.moveTo(config["doCommanderFunc"]["base_pos"], duration=DURATION)
        page, page_row = divmod(row, 2)
        for i in range(page * config["doCommanderFunc"]["scroll_times"] * 2):
            pyautogui.scroll(config["doCommanderFunc"]["scroll"])

        base_posX, base_posY = config["doCommanderFunc"]["base_pos"]
        base_posX += col * config["doCommanderFunc"]["col_space"]
        base_posY += page_row * config["doCommanderFunc"]["row_space"]

        pyautogui.click(base_posX, base_posY, duration=DURATION)
        pyautogui.sleep(2)
        # 获取途径按钮
        pyautogui.click(config['doCommanderFunc']
                        ['btngetcard'], duration=DURATION)

        # 扫荡
        pyautogui.moveTo(config['doCommanderFunc']['btnbatchbattle'])
        for i in range(0, config['doCommanderFunc']['btngetcard_scroll']):
            logger.debug("鼠标滚轮滚动第{}次，每次滚动{}单位。".format(i, 2))
            pyautogui.scroll(-2)
        pyautogui.click(config['doCommanderFunc']
                        ['btnbatchbattle'], duration=DURATION)
        result = True

    if mode == "exp":
        pyautogui.click(config['doCommanderFunc']['tab_exp'], duration=DURATION)

        # 增加经验 按钮
        # plusX, plusY = 1778, 241
        # pyautogui.click(config['doCommanderFunc']['exp_comander_pos'], duration=DURATION)

        # 使用经验道具
        # useX, useY = 1343, 498
        pyautogui.click(config['doCommanderFunc']['use'], duration=DURATION)

        # 选择第一个面试官
        pyautogui.click(config['doCommanderFunc']['exp_comander_pos'], duration=DURATION)

        # 输入使用几个经验道具
        # inputX, inputY = 971, 725
        pyautogui.click(config['doCommanderFunc']['input'], duration=DURATION)
        pyautogui.hotkey("CTRL", "A")
        pyautogui.write("1")
        #pyautogui.press("BACKSPACE", presses=6)

        # 应用
        enable = pxlMatch(
            *config['doCommanderFunc']['use2'], config['doCommanderFunc']['use2_rgb'], tolerance=10, comment="指挥官经验")
        if enable:
            pyautogui.click(config['doCommanderFunc']
                            ['use2'], duration=DURATION)
            result = True

    closeActiveWindow()
    return result


def checkHospital():
    """检查医院是否有伤兵，点击治疗"""
    logger.debug("检查医院是否有伤兵，点击治疗")
    pyautogui.press("Y")
    result = False
    enanble = pxlMatch(
        *config['checkHospital']['btntreat'], config['checkHospital']['btn_treat_rgb'], tolerance=10, comment="医院是否有伤兵")
    if enanble:
        # 治疗按钮
        pyautogui.click(config['checkHospital']['btntreat'], duration=DURATION)

        # 消耗资源确认按钮
        pyautogui.click(config['checkHospital']
                        ['btnconfirm'], duration=DURATION)
        result = True

    closeActiveWindow()
    return result


def stopBattle():
    """使用停战协议，判断失效了就立马开启防护罩"""
    # if STOP_BATTLE_ON:
    #     logger.debug("开始检查城堡的保护罩状态。。。")
    # else:
    #     logger.debug("系统设置不需要开启保护罩，退出检查。")
    #     return

    logger.debug("开始检查城堡的保护罩状态。。。")
    # 七神神堂
    pyautogui.press("B")
    # 停战协议入口
    # btnStopBattleX, btnStopBattleY = 1177, 498
    # 停战协议是否生效 搜索区域
    searchRegion = config['stopBattle']['searchregion']
    findPosCenter = pyautogui.locateCenterOnScreen(
        "scrShot/StopBattle.png", confidence=0.9, region=searchRegion)
    result = False
    if findPosCenter:
        logger.debug('停战协议还在生效中，不需要再使用。。。')
        closeActiveWindow()
        return True
    searchRegion = (SCREEN_MID_W, 0, SCREEN_MID_W, SCREEN_HEIGHT)
    findPosCenter = pyautogui.locateCenterOnScreen(
        "scrShot-browser/stopBattle2.png", confidence=0.9, region=searchRegion)
    if findPosCenter:
        pyautogui.click(findPosCenter, duration=DURATION)
        # 停战协议应用按钮
        enable = pxlMatch(
            *config['stopBattle']['btnused'], config['stopBattle']['btn_used_rgb'], tolerance=10, comment="停战协议")
        if enable:
            pyautogui.click(config['stopBattle']['btnused'], duration=DURATION)
            logger.debug('使用停战协议道具成功。')
            result = True
    else:
        logger.error('未搜索匹配到停战协议道具。')
        result = False

    closeActiveWindow()
    return result


def getEventsReward(events=1):
    """领取活动大厅奖励每日活跃度奖励
        events: 
            1: 活跃度奖励
            2：沙盘采集奖励
            3：野外首领奖励
            4：强兵富国奖励
            5：全力冲刺奖励
    """
    pyautogui.click(config['getEventsReward']
                    ['events_hall'], duration=DURATION)

    file_events = {
        1: "scrShot-browser/events_active.png",
        2: "scrShot-browser/events_gather.png",
        3: "scrShot-browser/events_leader.png",
        4: "scrShot-browser/events_strong.png",
        5: "scrShot-browser/events_besteffort.png", }

    findEvent = False
    times_out = config['getEventsReward']['times_out']
    for count in range(times_out):  # 查找活跃度坐标
        if findEvent == False:
            findPosCenter = pyautogui.locateCenterOnScreen(
                file_events[events], confidence=0.9)
            if findPosCenter:
                logger.debug("领取每日活跃度奖励。")
                pyautogui.click(findPosCenter, duration=DURATION)
                pyautogui.sleep(4)
                # 退出 循环查找
                findEvent = True
        if findEvent:
            break

        # 移动到窗口中间，鼠标滚轮事件
        pyautogui.moveTo(SCREEN_MID_W, SCREEN_MID_H, duration=DURATION)
        pyautogui.scroll(-10)

    if not findEvent:
        closeActiveWindow()
        return

    # 活跃度奖励
    if events == 1:
        # 循环领取活跃度奖励 顶多25个奖励
        for count in range(25):
            searchRegion = config['getEventsReward']['searchregion']
            findPosCenter = pyautogui.locateCenterOnScreen(
                'scrShot-browser/active_claim.png', confidence=0.9, region=searchRegion)
            # 无可领取的活跃度了，退出
            if not findPosCenter:
                break
            # 点击领取
            logger.debug("收集活跃度，第{}个。".format(count+1))
            pyautogui.click(findPosCenter, duration=DURATION)

        # 挨个领取宝箱，无论有没有
        baseX, baseY = config['getEventsReward']['active_reward_base']
        for count in range(6):
            logger.debug("点击领取活跃度奖励的第{}个宝箱。".format(count+1))
            pyautogui.click(baseX+92*count, baseY, duration=DURATION)
            if count == 2 or count == 5:
                pyautogui.click(config['getEventsReward']
                                ['active_roulette_go'], duration=DURATION)
                pyautogui.click(
                    config['getEventsReward']['active_roulette_close'], duration=DURATION)
    # 沙盘采集奖励
    elif events == 2:
        # 循环领取沙盘采集奖励 顶多5个奖励
        for count in range(5):
            searchRegion = config['getEventsReward']['region_gather']
            findPosCenter = pyautogui.locateCenterOnScreen(
                'scrShot-browser/active_claim.png', confidence=0.9, region=searchRegion)
            # 无可领取的活跃度了，退出
            if not findPosCenter:
                break
            # 点击领取
            logger.debug("收集沙盘采集奖励，第{}个。".format(count+1))
            pyautogui.click(findPosCenter, duration=DURATION)
    # 野外首领奖励
    elif events == 3:
        # 循环领取野外首领行动力消耗奖励 顶多5个奖励
        for count in range(5):
            searchRegion = config['getEventsReward']['region_gather']
            findPosCenter = pyautogui.locateCenterOnScreen(
                'scrShot-browser/active_claim.png', confidence=0.9, region=searchRegion)
            # 无可领取的活跃度了，退出
            if not findPosCenter:
                break
            # 点击领取
            logger.debug("收集野外首领行动力消耗奖励，第{}个。".format(count+1))
            pyautogui.click(findPosCenter, duration=DURATION)
    # 强兵富国奖励
    elif events == 4:
        # 循环强兵富国提升实力的奖励 顶多5个奖励
        for count in range(5):
            searchRegion = config['getEventsReward']['region_gather']
            findPosCenter = pyautogui.locateCenterOnScreen(
                'scrShot-browser/active_claim.png', confidence=0.9, region=searchRegion)
            # 无可领取的活跃度了，退出
            if not findPosCenter:
                break
            # 点击领取
            logger.debug("收集强兵富国提升实力的奖励，第{}个。".format(count+1))
            pyautogui.click(findPosCenter, duration=DURATION)
    # 5：全力冲刺奖励
    elif events == 5:
        # 循环获取全力冲刺奖励 顶多5个奖励
        for count in range(5):
            searchRegion = config['getEventsReward']['region_gather']
            findPosCenter = pyautogui.locateCenterOnScreen(
                'scrShot-browser/active_claim.png', confidence=0.9, region=searchRegion)
            # 无可领取的活跃度了，退出
            if not findPosCenter:
                break
            # 点击领取
            logger.debug("收集全力冲刺奖励的奖励，第{}个。".format(count+1))
            pyautogui.click(findPosCenter, duration=DURATION)

    closeActiveWindow()


def westerosNavigate():
    """维斯特洛航海者活动
    """
    pyautogui.click(config['westerosNavigate']['ship'], duration=DURATION)
    pyautogui.sleep(4)

    disable = pxlMatch(
        *config['westerosNavigate']['btn_nav'], config['westerosNavigate']['btn_nav_disable_rgb'], tolerance=10, comment="维斯特洛航海者活动")
    if disable:
        closeActiveWindow()
        return None

    searchRegion = config['westerosNavigate']['search_region']
    findPosCenter = pyautogui.locateCenterOnScreen(
        'scrShot-browser/Westeros_navigate.png', confidence=0.9, region=searchRegion)
    result = False
    # 无可领取的活跃度了，退出
    if findPosCenter:
        logger.debug("检查维斯特洛航海者活动，点击免费一次。")
        pyautogui.click(findPosCenter, duration=DURATION)
        result = True

    closeActiveWindow()
    return result


def gatherResources(pointX, pointY):
    '''在屏幕指定坐标进行资源的采集
    return
        True 采集成功
        False 失败
    '''
    # 根据指定的坐标，采集资源
    pyautogui.click(pointX, pointY, duration=DURATION)

    # 等待一会
    pyautogui.sleep(3)

    # 解析采集的资源的坐标位置
    txt = getText(
        textRegion=config['gatherResources']['ocr_region'], lang="eng")
    pos = pareseCoordinates(txt)

    # 该坐标是否允许采集
    if not permit(pos):
        closeActiveWindow()
        return False

    # 判断是不是可以采集。。。出现【回城按钮】表面自己还在采集
    searchRegion = config['gatherResources']['searchregion']

    # 校验是否搜索到资源
    findPosCenter = pyautogui.locateCenterOnScreen(
        'scrShot-browser/gather.png', confidence=0.9, region=searchRegion)

    # 资源还在
    if not findPosCenter:
        closeActiveWindow()
        return False

    # 点击采集按钮
    pyautogui.click(findPosCenter, duration=DURATION)

    # 派兵采集
    return sendTroops(dispatch=1)


def gatherAuto(ARMY_COUNT=1):
    """自动采集资源矿
    参数：
        ARMY_COUNT 同时派军队的数量"""
    resources = config['gatherAuto']['resources']
    level = config['gatherAuto']['level']

    i = 0
    while i < 7:
        i += 1
        idx = random.randint(0, (len(resources) - 1))
        searchSandBox(resources[idx], level=level[idx])
        # 判断给矿点是否已经拉线
        ret = isLine()
        if ret != 0:
            logger.debug("采集矿产资源，资源矿类型：{}, 级别：{}, 已经被其他人先拉线，返回码{}".format(
                resources[idx], level[idx], ret))
            break
        # 开始c采集矿产资源
        ret = gatherResources(SCREEN_MID_W, SCREEN_MID_H)
        if ret:
            ARMY_COUNT -= 1
        logger.debug("采集矿产资源，资源矿类型：{}, 级别：{}, 派遣军队批次{}, 派遣结果：{}".format(
            resources[idx], level[idx], i, ret))
        if ARMY_COUNT == 0:
            break


def setPause(seconds=300):
    """为了防止系统休眠，使用暂停函数后，让鼠标移动下，避免锁屏。
    在屏幕的中心区域 200，200pix范围内鼠标随机移动。
        seconds: 系统需要暂停的时间，单位为秒。
    """
    zoneX = SCREEN_MID_W - round(config['setPause']['zone_width']/2)
    # zoneY = SCREEN_MID_H - round(config['setPause']['zone_height']/2)
    zoneY = 0

    time_start = time.time()
    time_stop = 0
    # logger.debug("程序即将暂停{}秒。".format(seconds))
    while (time_stop - time_start) < seconds:
        # 设置暂停的时间太短，没必要设置睡眠时间。
        if seconds > config['setPause']['interval']:
            pyautogui.sleep(config['setPause']['interval'])

        offsetX = random.randint(0, config['setPause']['zone_width'])
        offsetY = random.randint(0, config['setPause']['zone_height'])
        pyautogui.moveTo(zoneX + offsetX, zoneY + offsetY, duration=DURATION)
        time_stop = time.time()
    # logger.debug("程序暂停{}秒，时间结束，继续运行。".format(seconds))


def isLine():
    '''
    判断屏幕中心的矿点是否已经被拉线了
    return
        0 没有被拉线
        1 被同盟成员拉线
        2 被非同盟成员拉线
    '''
    scrWidth, scrHeight = config['isLine']['line_zone']
    scrTopLeftX, scrTopLeftY = (
        SCREEN_WIDTH - scrWidth)/2, (SCREEN_HEIGHT - scrHeight)/2
    scrBottomRightX, scrBottomRightY = scrTopLeftX + scrWidth, scrTopLeftY + scrHeight
    # 屏幕截图 左上角和右小角形成的举行区域
    scr = ImageGrab.grab(
        (scrTopLeftX, scrTopLeftY, scrBottomRightX, scrBottomRightY))
    # scr.show()

    count = 0
    scrdata = np.array(scr)
    # 检查同盟拉线
    indices = np.where(np.all(scrdata == config['isLine']['alliance_rgb'], -1))
    count = indices[0].size
    if count > config['isLine']['threshold']:
        # scr.save("log/scr-1-{}.png".format(time.strftime("%Y%m%d%H%M%S")))  # debug
        return 1

    # 检查非同盟拉线
    indices = np.where(
        np.all(scrdata == config['isLine']['not_alliance_rgb'], -1))  # debug
    count = indices[0].size
    if count > config['isLine']['threshold']:
        # scr.save("log/scr-2-{}.png".format(time.strftime("%Y%m%d%H%M%S")))# debug
        return 2
    # scr.save("log/scr-0-{}.png".format(time.strftime("%Y%m%d%H%M%S")))  # debug
    return 0

def changeEquipment(type=1):
    '''
    更换装备套装
    type
        1 研发加速
        2 建筑加速
        3 采集加速
        4 战斗套装
        5 野外首领套装
    '''
    # 是否回到主界面，也就是有头像节目
    searchRegion = config['getFullScreen']['searchregion']
    findPos = pyautogui.locateOnScreen(
        "scrShot-browser/headImage.png", confidence=0.9, region=searchRegion)
    if findPos:
        pyautogui.click(findPos, duration=DURATION)
    else:
        logger.error("未能判断但是是否主界面。")
        return False
    
    # 点击进入预编套装选择页面
    pyautogui.click(config['changeEquipment']['btn_selected'], duration=DURATION)

    # 选择套装
    btn_str = "btn_{}".format(type)
    pyautogui.click(config['changeEquipment'][btn_str], duration=DURATION)

    # 关闭所有窗口
    closeActiveWindow()

    return True


def saveRunFlag(jsonObj):
    """保存运行参数，方便程序运行中断的时候能恢复"""
    # timestampe = time.strftime("%Y%m%d_%H_%M_%S")
    # fileName = "runFlag_{}.json".format(timestampe)
    fileName = "runFlag.json"
    # jsonStr = json.dumps(jsonObj)
    json.dump(jsonObj, open(fileName, "w"))


def getRunFlag():
    """返回上次运行时变量runFlag参数值。"""
    fileName = "runFlag.json"
    try:
        f = open(fileName, mode="r")
        content = f.read()
    except FileNotFoundError:
        content = ""

    if content:
        return json.loads(content)
    else:
        return None


def event_exit(runFlag):
    logger.debug("执行程序的退出事件,保存程序运行参数。")
    # 保存运行时的参数
    saveRunFlag(runFlag)


def CLI():
    """
    通过远程放入到程序目录下命令文件，从而控制程序。
    命令文件的格式内容目标{"cmd":args}
        cmd: 命令 args： 命令对应的参数
            restart  重启游戏
            pause  args 暂停游戏args秒

    """
    ret = False
    file_name = "command.json"
    cmd_file = pathlib.Path(file_name)
    logger.debug(f"检查目录下是否有命令文件:{file_name}。")
    # 目录下不存在
    if not cmd_file.exists():
        return False

    # json.load加载json格式文件，返回python对象
    f = cmd_file.open(mode='r', encoding='utf-8')
    data = json.load(f)
    f.close()

    for cmd, args in data.items():
        logger.debug(f"执行命令：{cmd}， 命令参数：{args}")
        if cmd == "restart":
            restartGame()

        if cmd == "pause":
            setPause(args)

        if cmd == "test":
            print(args)

    # 删除执行完的文件
    cmd_file.unlink()
    return ret


def do(cfg="config.ini"):
    '''
    开始执行自动化脚本任务
    '''
    # 记录每个任务的是否已经运行，或者上层运行的时间。
    runFlag = {
        "runDate": "",
        "city_runtime_queue": [],  # 下次切换城市地图的时间
        "sendMessage": {"runned": False},
        "claimChest": {"claim_counts": 0},
        "updateParamDict": {"next_runtime": 0},
        "getVIPPoints": {"runned": False},
        "claimSalary": {"runned": False},
        "getCOVID_bonus": {"runned": False},
        "allianceBoss": {"runned": False},
        "trainingGrounds1": {"runned": False},
        "trainingGrounds2": {"runned": False},
        "checkMerchantShip": {"ship_cycle": "20200202"},
        "checkTavernBonus": {},
        "claimAlliance": {},
        "doCommanderFunc": {"exp_runned": False},
        "getEventsReward": {},
        "checkHospital": {},
        "stopBattle": {},
        "openCityAdminWindow": {},
        "dragFunc": {"raffle_runned": False, "raffle_times": 0},
        "trainSoldiers": {},
        "checkQuest": {"vip_cycle": "20200202", "vip_runned": False, "vip_times": 0},
        "westerosNavigate": {"runned": False},
        "challengeAlliance": {"runned": False},
        "gatherAuto": {},
        "getChestOfWelfareCenter": {"runned": False},
        "attackRebel": {},  # 叛军据点
        "attackRebelLeader": {},  # 野外叛军首领
    }

    # 首次运行的时候，如有有上次保存的运行参数，则读取【使用Python 3.8版本海象运算符】
    # if lastRunFlag := getRunFlag(): runFlag.update(lastRunFlag)
    # 降级为3.7版本运行
    lastRunFlag = getRunFlag()
    if lastRunFlag:
        runFlag.update(lastRunFlag)

    # 把所有的配置参数读取到内存变量中
    global config, DURATION, G_MAP_MODE

    ARMY_LIMIT = 5  # 行军队列上限数量
    loop_times = 0  # 循环次数

    # 注册一个退出事件,
    atexit.register(event_exit, runFlag)

    # 循环执行任务
    while True:
        task_No = 0
        # 读取配置文件,因为函数都引用config，必须要最先初始化,随后间隔一段事件重新读取
        now = round(time.time())
        if now > runFlag["updateParamDict"]["next_runtime"] or loop_times == 0:
            logger.info("任务%d：【读取配置文件参数】在第%d次循环。", task_No, loop_times)
            config = updateParamDict(cfg)
            # 全局鼠标移动的时间
            DURATION = config['Global']['duration']
            # 初始化参数
            pyautogui.PAUSE = config['Global']['pause']
            runFlag["updateParamDict"]["next_runtime"] = now + \
                config["updateParamDict"]["update_interval"]

        # 执行远程命令 2021年1月26日 16点41分 废弃这个功能
        # ret = CLI()
        # task_No += 1
        # logger.info("任务%d：【检查远程命令文件】，执行状态：【%s】。", task_No, ret)

        # 在暂停区域停留，让程序暂停
        # curPosX, curPosY = pyautogui.position()
        # if (curPosX + config['Global']['pause_zone_w'])> SCREEN_WIDTH and curPosY < config['Global']['pause_zone_h']:
        #     ret = pyautogui.prompt(text="程序即将暂停,清输入暂停的时间(秒)。", title='程序暂停' , default=config['Global']['pause_zone_sec'])
        #     if ret and ret.isnumeric():
        #         setPause(int(ret))

        # 如果帐户在被的地方登陆了，则休眠1个小时。
        G_MAP_MODE = getMapMode()
        if G_MAP_MODE == "offline":
            logger.info("帐户在其它客户端登陆了，自动化脚本开始休眠1个小时。")
            setPause(3600)
            logger.info("自动化脚本休眠1个小时结束。")
            restartGame()

        epoch_sec = round(time.time())
        gmTime = time.gmtime(epoch_sec)
        localTime = time.localtime(epoch_sec)
        todayStr = time.strftime("%Y%m%d", gmTime)  # 使用的是GMT时间

        if G_MAP_MODE == "nothing":
            setWindowFocus()  # 让窗口保持焦点
        
        if not setFullScreen(True): 
            restartGame()
            continue  # 保持浏览器全屏

        # 执行循环任务
        loopTimeStart = epoch_sec
        logger.info("【开始】第{}次循环定时任务执行。".format(loop_times))

        # 重置运行参数标识 UTC 0点 也就是北京时间每天早上8点，系统重置
        if (todayStr != runFlag["runDate"]):  # 或者读取运行参数文件失败的时候也重置参数
            # 记录每个任务的是否已经运行，或者上层运行的时间。
            runFlag["runDate"] = todayStr
            runFlag["sendMessage"]["runned"] = False  # 是否已经运行过
            runFlag["getVIPPoints"]["runned"] = False  # 是否已经运行过
            runFlag["claimSalary"]["runned"] = False  # 是否已经运行过
            runFlag["getCOVID_bonus"]["runned"] = False  # 是否已经运行过
            runFlag["allianceBoss"]["runned"] = False  # 是否已经运行过
            runFlag["trainingGrounds1"]["runned"] = False  # 是否已经运行过
            runFlag["trainingGrounds2"]["runned"] = False  # 游兵远征
            runFlag["doCommanderFunc"]["exp_runned"] = False  # 升级指挥官经验
            runFlag["westerosNavigate"]["runned"] = False  # 维斯特洛航海活动
            runFlag["challengeAlliance"]["runned"] = False  # 联盟试炼挑战
            runFlag["getChestOfWelfareCenter"]["runned"] = False  # 联盟试炼挑战
            runFlag["dragFunc"]["raffle_runned"] = False  # 龙技能碎片抽取
            runFlag["dragFunc"]["raffle_times"] = 0  # 龙技能碎片抽取次数
            runFlag["city_runtime_queue"].append(
                epoch_sec)  # 重置了,就把需城市地图的执行时间加入队列

        # VIP任务宝箱是每天 UTC 4点 北京时间12点重置
        vip_cycle = time.strftime("%Y%m%d", time.gmtime(epoch_sec - 14400))
        if vip_cycle != runFlag["checkQuest"]["vip_cycle"]:
            runFlag["checkQuest"]["vip_cycle"] = vip_cycle
            runFlag["checkQuest"]["vip_runned"] = False  # VIP任务宝箱全部领取完毕
            runFlag["checkQuest"]["vip_times"] = 0  # VIP任务宝箱领取个数
            runFlag["city_runtime_queue"].append(
                epoch_sec)  # 重置了,就把需城市地图的执行时间加入队列

        # 检查商船 每隔6个小时刷新货物
        now = round(time.time())
        localTime_ship = time.localtime(now - 1200)  # 每0，6，12，18 点的18分左右重置
        div, mod = divmod(localTime_ship.tm_hour, 6)
        mod = mod - 0
        ship_cycle = "{}{}-{}".format(localTime_ship.tm_mon,
                                      localTime_ship.tm_mday, div)
        # 每隔6小时重置
        if ship_cycle != runFlag["checkMerchantShip"]["ship_cycle"]:
            runFlag["checkMerchantShip"]["ship_cycle"] = ship_cycle
            runFlag["checkMerchantShip"]["runned"] = False
            runFlag["city_runtime_queue"].append(
                epoch_sec)  # 重置了,就把需城市地图的执行时间加入队列

        # 必须要先执行，这样后面的任务才能根据派出的军队数量来安排。
        ARMY_COUNT = getArmyNum()  # 现在派出多少支军队

        # 如果还有拍卖战利品箱子的空位置且行军队列小于5，则可以自动派出去参与集结攻击
        task_No += 1
        if config["rallyAttack"]["switch"] and ARMY_COUNT < config["Global"]["march_limit"]:
            ret = rallyAttack()
            logger.info("任务%d：【参与联盟集结攻击】，执行状态：【%s】。", task_No, ret)
            if ret:
                ARMY_COUNT += 1
                runFlag["checkHospital"]["next_runtime"] = now + 1200

        # 每晚19-21点少派一支军队出去采集。
        beginHour, beginMin, endHour, endMin = config["Global"]["march_schedule"]
        beginMins = beginHour * 60 + beginMin
        endMins = endHour * 60 + endMin
        nowMins = localTime.tm_hour * 60 + localTime.tm_min
        if nowMins >= beginMins and nowMins <= endMins:
            ARMY_LIMIT = config["Global"]["march_schedule_count"]
        else:
            ARMY_LIMIT = config["Global"]["march_limit"]

        # ******************************************************************
        # 以下任务的地图模式都是   ***any***。
        # ******************************************************************

        task_No += 1
        if claimChest(G_MAP_MODE):  # 收集宝箱
            runFlag["claimChest"]["claim_counts"] += 1
            logger.info("任务%d：【检查领取神秘宝箱】，执行状态：成功领取神秘宝箱%d次。",
                        task_No, runFlag["claimChest"]["claim_counts"])

        task_No += 1
        ret = helpAlliance()  # 联盟帮助
        logger.info("任务%d：【检查联盟帮助】，执行状态：【%s】。", task_No, ret)

        # 每日任务 免费龙技能碎片6个， 每隔5分钟
        task_No += 1
        now = round(time.time())
        next_runtime = runFlag["dragFunc"].get("raffle_next_runtime", 0)
        if not runFlag["dragFunc"]["raffle_runned"] and now > next_runtime:
            ret = dragFunc("raffle")
            logger.info("任务%d：【抽取龙技能碎片】，执行状态：【%s】。", task_No, ret)
            runFlag["dragFunc"]["raffle_next_runtime"] = now + 300  # 5 mins
            if ret:
                runFlag["dragFunc"]["raffle_times"] += 1

            # 如果成功的次数6次，则可以判定今天的免费次数用完了
            if runFlag["dragFunc"]["raffle_times"] == 6:
                runFlag["dragFunc"]["raffle_runned"] = True

        # 龙游历
        task_No += 1
        now = round(time.time())
        next_runtime = runFlag["dragFunc"].get("explore_next_runtime", 0)
        # 龙游历3小时=10800秒
        if now > next_runtime:
            ret = dragFunc("explore")
            logger.info("任务%d：【龙游历】%d小时，执行状态：【%s】。", task_No,
                        config['dragFunc']['explore_duration'], ret)
            if ret:
                runFlag["dragFunc"]["explore_next_runtime"] = now + \
                    config['dragFunc']['explore_duration'] * 3600  # 根据配置的时长计算

        # 使用龙1技能天赋【建造加速】冷却时间24个小时
        task_No += 1
        now = round(time.time())
        next_runtime = runFlag["dragFunc"].get("talent1_B_next_runtime", 0)
        # 达到设定的间隔时间，开始执行任务
        if now > next_runtime:
            ret = dragFunc("talentB")
            logger.info("任务%d：【执行龙1技能天赋 建造加速】，执行状态：【%s】。", task_No, ret)
            if ret:
                runFlag["dragFunc"]["talent1_B_next_runtime"] = now + \
                    config["dragFunc"]["talent1_interval"]

        # 使用龙1技能天赋【研发加速】冷却时间24个小时
        task_No += 1
        now = round(time.time())
        next_runtime = runFlag["dragFunc"].get("talent1_R_next_runtime", 0)
        # 达到设定的间隔时间，开始执行任务
        if now > next_runtime:
            ret = dragFunc("talentR")
            logger.info("任务%d：【执行龙1技能天赋 研发加速】，执行状态：【%s】。", task_No, ret)
            if ret:
                runFlag["dragFunc"]["talent1_R_next_runtime"] = now + \
                    config["dragFunc"]["talent1_interval"]

        # 使用龙2技能天赋 冷却时间72个小时
        task_No += 1
        now = round(time.time())
        next_runtime = runFlag["dragFunc"].get("talent2G_next_runtime", 0)
        # 达到设定的间隔时间，开始执行任务
        if now > next_runtime and ARMY_COUNT > 3:
            ret = dragFunc("talent2G")
            logger.info("任务%d：【执行龙2技能天赋 快速采集】，执行状态：【%s】。", task_No, ret)
            if ret:
                runFlag["dragFunc"]["talent2G_next_runtime"] = now + \
                    config["dragFunc"]["talent2g_interval"]

        # 检查任务列表 联盟任务
        task_No += 1
        now = round(time.time())
        next_runtime = runFlag["checkQuest"].get("next_runtime", 0)
        # 达到设定的间隔时间，开始执行任务
        if now > next_runtime:
            ret = checkQuest("alliance")
            logger.info("任务%d：【联盟任务】，执行状态：【%s】。", task_No, ret)
            if ret:
                runFlag["checkQuest"]["next_runtime"] = now + \
                    config["checkQuest"]["alliance_interval"]

        # 检查任务列表 VIP任务奖励宝箱任务 每隔1小时重置
        task_No += 1
        now = round(time.time())
        next_runtime = runFlag["checkQuest"].get("vip_next_runtime", 0)
        if not runFlag["checkQuest"]["vip_runned"] and now > next_runtime:
            ret = checkQuest("vip")
            logger.info("任务%d：【联盟VIP宝箱领取任务】，执行状态：【%s】。", task_No, ret)
            if ret:
                runFlag["checkQuest"]["vip_times"] += 1
                runFlag["checkQuest"]["vip_next_runtime"] = now + \
                    config["checkQuest"]["vip_interval"]

            # 如果成功的次数5次，则可以判定今天VIP宝箱领取完了
            if runFlag["checkQuest"]["vip_times"] == config["checkQuest"]["box_count"]:
                runFlag["checkQuest"]["vip_runned"] = True

        # 检查医院是否有需要医疗的伤兵 每隔个小时
        task_No += 1
        now = round(time.time())
        next_runtime = runFlag["checkHospital"].get("next_runtime", 0)
        # 达到设定的间隔时间，开始执行任务
        if now > next_runtime:
            ret = checkHospital()
            logger.info("任务%d：【检查医院伤兵】，执行状态：【%s】。", task_No, ret)
            runFlag["checkHospital"]["next_runtime"] = now + \
                config["checkHospital"]["check_interval"]

        # 联盟礼物
        task_No += 1
        now = round(time.time())
        next_runtime = runFlag["claimAlliance"].get("next_runtime", 0)
        # 达到设定的间隔时间，开始执行任务
        if now > next_runtime:
            ret = claimAlliance()
            logger.info("任务%d：【收集联盟礼物】，执行状态：【%s】。", task_No, ret)
            runFlag["claimAlliance"]["next_runtime"] = now + \
                config["claimAlliance"]["check_interval"]

        # 获取指挥官勋章卡片
        task_No += 1
        now = round(time.time())
        next_runtime = runFlag["doCommanderFunc"].get("next_runtime", 0)
        # 达到设定的间隔时间，开始执行任务
        if now > next_runtime:
            ret = doCommanderFunc(
                "card", *config["doCommanderFunc"]["card_comander_pos"])
            logger.info("任务%d：【获取指挥官勋章卡片】，执行状态：【%s】。", task_No, ret)
            if ret:
                runFlag["doCommanderFunc"]["next_runtime"] = now + \
                    config["doCommanderFunc"]["card_interval"]

        # ******************************************************************
        # 以下任务的地图模式都是   ***city***。
        # ******************************************************************
        now = round(time.time())
        city_next_runtime = min(runFlag["city_runtime_queue"]) if len(
            runFlag["city_runtime_queue"]) > 0 else 0
        if now > city_next_runtime and switchMapMode("city"):
            G_MAP_MODE = "city"
            runFlag["city_runtime_queue"].remove(city_next_runtime)  # 删除执行过的时间
            setTopView()

            # 是否要开启停战协议
            now = round(time.time())
            next_runtime = runFlag["stopBattle"].get("next_runtime", 0)
            if config['Global']['stop_battle_on'] and now > next_runtime and stopBattle():
                # 停战协议 stopBattle 间隔4个小时
                runFlag["stopBattle"]["next_runtime"] = now + \
                    config['stopBattle']['check_interval']
                runFlag["city_runtime_queue"].append(
                    runFlag["stopBattle"]["next_runtime"])  # 下次执行时间追加到执行队列中

            # 兵种训练成功，则下次训练下一个兵种
            task_No += 1
            now = round(time.time())
            next_runtime = runFlag["trainSoldiers"].get("next_runtime", 0)
            # 达到设定的间隔时间，开始执行任务
            if now > next_runtime:
                # mauler 盾兵, cavalry 骑兵, bowman 弓兵, spearman 枪兵
                arms = ["mauler", "cavalry", "spearman", "bowman"]
                NEXT_ARMS_IDX = runFlag["trainSoldiers"].get(
                    "NEXT_ARMS_IDX", random.randint(0, 3))
                succ = trainSoldiers(arms[NEXT_ARMS_IDX], num=500, level=4)
                # 本次兵种训练成功，则下次训练下一个兵种
                if succ:
                    NEXT_ARMS_IDX = (NEXT_ARMS_IDX + 1) % 4  # 保证索引不越界
                    # logger.info("本次训练的兵种【{}】成功，则下次训练的兵种【{}】。".format(arms[NEXT_ARMS_IDX - 1], arms[NEXT_ARMS_IDX]))
                    runFlag["trainSoldiers"]["NEXT_ARMS_IDX"] = NEXT_ARMS_IDX
                    runFlag["trainSoldiers"]["next_runtime"] = now + \
                        config["trainSoldiers"]["check_interval"]

                logger.info("任务%d：【训练士兵】，本次训练的兵种：%s，下次训练的兵种：%s，执行状态：【%s】。",
                            task_No, arms[NEXT_ARMS_IDX - 1], arms[NEXT_ARMS_IDX], ret)
                runFlag["city_runtime_queue"].append(
                    runFlag["trainSoldiers"]["next_runtime"])  # 下次执行时间追加到执行队列中

            # 拍卖战利品 每隔2个小时
            task_No += 1
            now = round(time.time())
            next_runtime = runFlag["openCityAdminWindow"].get(
                "next_runtime", 0)
            # 达到设定的间隔时间，开始执行任务
            if now > next_runtime:
                openCityAdminWindow(6) #酒馆奖励
                ret = openCityAdminWindow(2) #战利品拍卖
                if ret:
                    runFlag["openCityAdminWindow"]["next_runtime"] = now + \
                        config["openCityAdminWindow"]["check_interval2"]
                else:  # 执行失败则每个小时检查一次
                    runFlag["openCityAdminWindow"]["next_runtime"] = now + 3600

                logger.info("任务%d：【拍卖战利品】，执行状态：【%s】。", task_No, ret)
                runFlag["city_runtime_queue"].append(
                    runFlag["openCityAdminWindow"]["next_runtime"])  # 下次执行时间追加到执行队列中

            # 达到设定的间隔时间，开始执行任务
            task_No += 1
            if not runFlag["checkMerchantShip"]["runned"]:
                ret = checkMerchantShip()
                if ret:
                    runFlag["checkMerchantShip"]["runned"] = True
                else:
                    runFlag["city_runtime_queue"].append(0)

                logger.info("任务%d：【商船购买物品】，执行状态：【%s】。", task_No, ret)

            # 酒馆奖励 每隔4个小时增加一次
            task_No += 1
            now = round(time.time())
            next_runtime = runFlag["checkTavernBonus"].get("next_runtime", 0)
            # 达到设定的间隔时间，开始执行任务
            if now > next_runtime:
                checkTavernBonus()
                logger.info("任务%d：【领取酒馆奖励】，执行状态：【%s】。", task_No, "None")
                runFlag["checkTavernBonus"]["next_runtime"] = now + \
                    config["checkTavernBonus"]["check_interval"]

                runFlag["city_runtime_queue"].append(
                    runFlag["checkTavernBonus"]["next_runtime"])  # 下次执行时间追加到执行队列中

            # 在每日6点之前，把活跃度奖励收集完
            task_No += 1
            now = round(time.time())
            next_runtime = runFlag["getEventsReward"].get("next_runtime", 0)
            # 达到设定的间隔时间，开始执行任务
            if now > next_runtime and localTime.tm_hour < 6:
                getEventsReward(1)  # 活跃度奖励领取
                getEventsReward(2)
                getEventsReward(3)
                getEventsReward(4)
                getEventsReward(5)
                logger.info(
                    "任务%d：【活跃度、沙盘采集、野外首领、强兵富国的奖励领取】，执行状态：【%s】。", task_No, "None")
                runFlag["getEventsReward"]["next_runtime"] = now + \
                    config["getEventsReward"]["check_interval"]
                runFlag["city_runtime_queue"].append(
                    runFlag["getEventsReward"]["next_runtime"])  # 下次执行时间追加到执行队列中

            # 每日AM 8点是系统重置，每日例行任务 >>>发送一条消息
            task_No += 1
            if not runFlag["sendMessage"]["runned"]:
                ret = sendMessage()
                logger.info("任务%d：【每日任务>在世界频道发送一条消息】，执行状态：【%s】。", task_No, ret)
                if ret:
                    # logger.info("每日任务>>>在世界频道发送一条消息，发送成功。")
                    runFlag["sendMessage"]["runned"] = True

            # 每日AM 8点是系统重置，每日例行任务 >>>领取官爵每日俸禄
            task_No += 1
            if not runFlag["claimSalary"]["runned"]:
                ret = claimSalary()
                logger.info("任务%d：【每日任务>领取官爵每日俸禄】，执行状态：【%s】。", task_No, ret)
                if ret:
                    # logger.info("每日任务>>>领取官爵每日俸禄，领取成功。")
                    runFlag["claimSalary"]["runned"] = True

            # 每日AM 8点是系统重置，每日例行任务 >>>领取VIP每日积分，每日免费宝箱
            task_No += 1
            if not runFlag["getVIPPoints"]["runned"]:
                ret = getVIPPoints()
                logger.info("任务%d：【每日任务>领取VIP每日积分】，执行状态：【%s】。", task_No, ret)
                if ret:
                    # logger.info("每日任务>>>领取VIP每日积分，每日免费宝箱，领取成功。")
                    runFlag["getVIPPoints"]["runned"] = True

            # 每日AM 8点是系统重置，每日例行任务 >>>领取COVID-19疫情期间每日的奖励
            task_No += 1
            if not runFlag["getCOVID_bonus"]["runned"]:
                ret = getCOVID_bonus()
                logger.info(
                    "任务%d：【每日任务>领取COVID-19疫情期间每日的奖励】，执行状态：【%s】。", task_No, ret)
                if ret:
                    # logger.info("每日任务>>>领取COVID-19疫情期间每日的奖励，领取成功。")
                    runFlag["getCOVID_bonus"]["runned"] = True

            # 每日AM 8点是系统重置，每日例行任务 >>>联盟Boss扫荡
            task_No += 1
            if not runFlag["allianceBoss"]["runned"]:
                ret = openCityAdminWindow(1)  # 联盟Boss扫荡
                logger.info("任务%d：【每日任务>联盟Boss扫荡】，执行状态：【%s】。", task_No, ret)
                if ret:
                    # logger.info("每日任务>>>联盟Boss扫荡，执行成功。")
                    runFlag["allianceBoss"]["runned"] = True

            # 每日AM 8点是系统重置，每日例行任务 >>>演武场指挥官挑战
            task_No += 1
            if not runFlag["trainingGrounds1"]["runned"]:
                ret = trainingGrounds(1)
                logger.info("任务%d：【每日任务>演武场指挥官挑战】，执行状态：【%s】。", task_No, ret)
                if ret:
                    # logger.info("每日任务>>>演武场指挥官挑战，执行成功。")
                    runFlag["trainingGrounds1"]["runned"] = True

            # 每日AM 8点是系统重置，每日例行任务 >>>游兵远征活动
            task_No += 1
            if not runFlag["trainingGrounds2"]["runned"]:
                ret = trainingGrounds(2)
                logger.info("任务%d：【每日任务>游兵远征活动】，执行状态：【%s】。", task_No, ret)
                if ret:
                    # logger.info("每日任务>>>游兵远征活动，执行成功。")
                    runFlag["trainingGrounds2"]["runned"] = True

            # 每日AM 8点是系统重置，每日例行任务 >>>升级指挥官经验
            task_No += 1
            if not runFlag["doCommanderFunc"]["exp_runned"]:
                ret = doCommanderFunc(
                    "exp", *config["doCommanderFunc"]["exp_comander_pos"])  # 升级指挥官经验
                logger.info("任务%d：【每日任务>升级指挥官经验】，执行状态：【%s】。", task_No, ret)
                if ret:
                    # logger.info("每日任务>>>升级指挥官经验，执行成功。")
                    runFlag["doCommanderFunc"]["exp_runned"] = True

            # 每日AM 8点是系统重置，每日例行任务 >>>维斯特洛航海者活动
            task_No += 1
            if not runFlag["westerosNavigate"]["runned"]:
                ret = westerosNavigate()  # 维斯特洛航海者活动
                logger.info("任务%d：【每日任务>维斯特洛航海者活动】，执行状态：【%s】。", task_No, ret)
                if ret or ret == None:
                    # logger.info("每日任务>>>维斯特洛航海者活动，执行成功。")
                    runFlag["westerosNavigate"]["runned"] = True

            # 每日AM 8点是系统重置，每日例行任务 >>>联盟试炼挑战
            task_No += 1
            if not runFlag["challengeAlliance"]["runned"]:
                ret = challengeAlliance()  # 联盟试炼挑战
                logger.info("任务%d：【每日任务>联盟试炼挑战】，执行状态：【%s】。", task_No, ret)
                if ret:
                    # logger.info("每日任务>>>联盟试炼挑战，执行成功。")
                    runFlag["challengeAlliance"]["runned"] = True

            # 每日AM 8点是系统重置，每日例行任务 >>>领取福利中心免费宝箱
            task_No += 1
            if not runFlag["getChestOfWelfareCenter"]["runned"]:
                ret = getChestOfWelfareCenter()  # 领取福利中心免费宝箱
                logger.info("任务%d：【每日任务>领取福利中心免费宝箱】，执行状态：【%s】。", task_No, ret)
                if ret:
                    # logger.info("每日任务>>>领取福利中心免费宝箱，执行成功。")
                    runFlag["getChestOfWelfareCenter"]["runned"] = True

        # ******************************************************************
        # 以下任务的地图模式都是   ***world***。
        # ******************************************************************
        logger.debug(
            f"开始进入world地图模式ARMY_COUNT={ARMY_COUNT}, ARMY_LIMIT={ARMY_LIMIT}")
        if ARMY_COUNT < ARMY_LIMIT and switchMapMode("world"):
            G_MAP_MODE = "world"
            # 攻击叛军据点
            now = round(time.time())
            next_runtime = runFlag["attackRebel"].get("next_runtime", 0)
            if now > next_runtime:
                # 如果很容易发现精英叛军那么连续多来几次，最多7次,精力上限1500
                for i in range(7):
                    # 精英叛军
                    ret = attackRebel(15, True)
                    if ret >= 0:
                        logger.info(f"攻击叛军据点，连续攻击第{i}次。")
                        setPause(160)  # 等待军队返回

                    if ret != 0:
                        runFlag["attackRebel"]["next_runtime"] = now + \
                            config["attackRebel"]["rebel_interval"]
                        break

            # 攻击野外叛军首领
            now = round(time.time())
            next_runtime = runFlag["attackRebelLeader"].get("next_runtime", 0)
            if now > next_runtime:
                attack_level, attack_times = config["attackRebelLeader"]["attack_level_times"]
                ret = attackRebelLeader(attack_level, attack_times)
                if ret:
                    runFlag["attackRebelLeader"]["next_runtime"] = now + \
                        config["attackRebelLeader"]["leader_interval"]
                    setPause(90)  # 等待军队返回 #等待军队返回

            # 采集矿场资源
            now = round(time.time())
            next_runtime = runFlag["gatherAuto"].get("next_runtime", 0)
            if now > next_runtime:
                gatherAuto(ARMY_LIMIT - ARMY_COUNT)
                runFlag["gatherAuto"]["next_runtime"] = now + \
                    config["gatherAuto"]["check_interval"]

        if claimChest(G_MAP_MODE):  # 收集宝箱
            runFlag["claimChest"]["claim_counts"] += 1
            logger.info("成功领取神秘宝箱%d次。", runFlag["claimChest"]["claim_counts"])
        
        helpAlliance()  # 联盟帮助

        loopTimeStop = round(time.time())
        loopTimeCost = loopTimeStop - loopTimeStart
        logger.info("【结束】第{}次循环定时任务执行。本次循环任务总共耗时【{:.2f}】秒。".format(
            loop_times, loopTimeCost))
        logger.debug("保存运行的参数runFlag: %s", json.dumps(runFlag))

        loop_times += 1
        setPause(120)  # 间隔2分钟循环


# 全局参素配置
config = {}
# 全局鼠标移动的时间
DURATION = 0  # 默认值
SCREEN_WIDTH, SCREEN_HEIGHT = pyautogui.size()
SCREEN_MID_W, SCREEN_MID_H = int(SCREEN_WIDTH/2), int(SCREEN_HEIGHT/2)  # 屏幕中间

if __name__ == "__main__":
    fire.Fire({
        "do": do})
